summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/bus.c11
-rw-r--r--drivers/acpi/pci_link.c2
-rw-r--r--drivers/acpi/system.c13
-rw-r--r--drivers/base/Makefile3
-rw-r--r--drivers/base/attribute_container.c9
-rw-r--r--drivers/base/base.h64
-rw-r--r--drivers/base/bus.c501
-rw-r--r--drivers/base/class.c319
-rw-r--r--drivers/base/core.c582
-rw-r--r--drivers/base/cpu.c2
-rw-r--r--drivers/base/dd.c164
-rw-r--r--drivers/base/driver.c216
-rw-r--r--drivers/base/firmware.c26
-rw-r--r--drivers/base/hypervisor.c12
-rw-r--r--drivers/base/init.c10
-rw-r--r--drivers/base/memory.c2
-rw-r--r--drivers/base/module.c94
-rw-r--r--drivers/base/node.c2
-rw-r--r--drivers/base/platform.c239
-rw-r--r--drivers/base/power/Makefile1
-rw-r--r--drivers/base/power/main.c502
-rw-r--r--drivers/base/power/power.h19
-rw-r--r--drivers/base/power/shutdown.c48
-rw-r--r--drivers/base/sys.c49
-rw-r--r--drivers/block/aoe/aoeblk.c51
-rw-r--r--drivers/block/aoe/aoechr.c7
-rw-r--r--drivers/block/nbd.c15
-rw-r--r--drivers/block/paride/pg.c6
-rw-r--r--drivers/block/paride/pt.c12
-rw-r--r--drivers/block/pktcdvd.c27
-rw-r--r--drivers/char/Kconfig10
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/hvc_console.c80
-rw-r--r--drivers/char/hvcs.c78
-rw-r--r--drivers/char/hw_random/amd-rng.c12
-rw-r--r--drivers/char/hw_random/core.c24
-rw-r--r--drivers/char/hw_random/geode-rng.c12
-rw-r--r--drivers/char/hw_random/intel-rng.c15
-rw-r--r--drivers/char/hw_random/omap-rng.c13
-rw-r--r--drivers/char/hw_random/pasemi-rng.c16
-rw-r--r--drivers/char/hw_random/via-rng.c19
-rw-r--r--drivers/char/nozomi.c1993
-rw-r--r--drivers/cpufreq/cpufreq.c12
-rw-r--r--drivers/cpuidle/sysfs.c21
-rw-r--r--drivers/crypto/Kconfig11
-rw-r--r--drivers/crypto/Makefile1
-rw-r--r--drivers/crypto/geode-aes.c298
-rw-r--r--drivers/crypto/geode-aes.h44
-rw-r--r--drivers/crypto/hifn_795x.c2838
-rw-r--r--drivers/crypto/padlock-aes.c24
-rw-r--r--drivers/dma/dmaengine.c43
-rw-r--r--drivers/edac/edac_device_sysfs.c49
-rw-r--r--drivers/edac/edac_mc_sysfs.c40
-rw-r--r--drivers/edac/edac_module.c2
-rw-r--r--drivers/edac/edac_pci_sysfs.c35
-rw-r--r--drivers/firmware/edd.c25
-rw-r--r--drivers/firmware/efivars.c130
-rw-r--r--drivers/i2c/chips/isp1301_omap.c6
-rw-r--r--drivers/ide/ide-probe.c2
-rw-r--r--drivers/ide/ide-tape.c14
-rw-r--r--drivers/ieee1394/nodemgr.c314
-rw-r--r--drivers/infiniband/core/sysfs.c39
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c13
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c17
-rw-r--r--drivers/infiniband/hw/ipath/ipath_kernel.h3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_sysfs.c30
-rw-r--r--drivers/isdn/capi/capi.c6
-rw-r--r--drivers/isdn/capi/capidrv.c9
-rw-r--r--drivers/isdn/gigaset/gigaset.h6
-rw-r--r--drivers/kvm/kvm_main.c2
-rw-r--r--drivers/macintosh/adb.c2
-rw-r--r--drivers/macintosh/via-pmu.c2
-rw-r--r--drivers/md/dm.c4
-rw-r--r--drivers/md/md.c31
-rw-r--r--drivers/mfd/ucb1x00-assabet.c17
-rw-r--r--drivers/mfd/ucb1x00-core.c14
-rw-r--r--drivers/mfd/ucb1x00.h4
-rw-r--r--drivers/misc/ibmasm/command.c12
-rw-r--r--drivers/misc/ibmasm/ibmasm.h10
-rw-r--r--drivers/misc/tifm_7xx1.c4
-rw-r--r--drivers/misc/tifm_core.c24
-rw-r--r--drivers/mtd/mtdchar.c12
-rw-r--r--drivers/net/ibmveth.c12
-rw-r--r--drivers/net/iseries_veth.c19
-rw-r--r--drivers/net/wan/cosa.c5
-rw-r--r--drivers/parisc/pdc_stable.c194
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c4
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c39
-rw-r--r--drivers/pci/hotplug/rpadlpar_sysfs.c113
-rw-r--r--drivers/pci/pci-driver.c59
-rw-r--r--drivers/pci/probe.c11
-rw-r--r--drivers/pcmcia/ds.c3
-rw-r--r--drivers/power/apm_power.c116
-rw-r--r--drivers/power/power_supply_core.c74
-rw-r--r--drivers/rtc/interface.c22
-rw-r--r--drivers/s390/net/netiucv.c16
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c14
-rw-r--r--drivers/s390/scsi/zfcp_ext.h3
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_driver.c27
-rw-r--r--drivers/scsi/hosts.c24
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c2
-rw-r--r--drivers/serial/icom.c24
-rw-r--r--drivers/serial/icom.h2
-rw-r--r--drivers/spi/spi.c26
-rw-r--r--drivers/uio/uio.c95
-rw-r--r--drivers/usb/core/driver.c8
106 files changed, 7867 insertions, 2496 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index f4487c38d9f2..1b4cf984b081 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -743,7 +743,7 @@ static int __init acpi_bus_init(void)
return -ENODEV;
}
-decl_subsys(acpi, NULL, NULL);
+struct kobject *acpi_kobj;
static int __init acpi_init(void)
{
@@ -755,10 +755,11 @@ static int __init acpi_init(void)
return -ENODEV;
}
- result = firmware_register(&acpi_subsys);
- if (result < 0)
- printk(KERN_WARNING "%s: firmware_register error: %d\n",
- __FUNCTION__, result);
+ acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
+ if (!acpi_kobj) {
+ printk(KERN_WARNING "%s: kset create error\n", __FUNCTION__);
+ acpi_kobj = NULL;
+ }
result = acpi_bus_init();
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
index c9f526e55392..5400ea173f6f 100644
--- a/drivers/acpi/pci_link.c
+++ b/drivers/acpi/pci_link.c
@@ -911,7 +911,7 @@ __setup("acpi_irq_balance", acpi_irq_balance_set);
/* FIXME: we will remove this interface after all drivers call pci_disable_device */
static struct sysdev_class irqrouter_sysdev_class = {
- set_kset_name("irqrouter"),
+ .name = "irqrouter",
.resume = irqrouter_resume,
};
diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c
index edee2806e37b..5ffe0ea18967 100644
--- a/drivers/acpi/system.c
+++ b/drivers/acpi/system.c
@@ -58,7 +58,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);
FS Interface (/sys)
-------------------------------------------------------------------------- */
static LIST_HEAD(acpi_table_attr_list);
-static struct kobject tables_kobj;
+static struct kobject *tables_kobj;
struct acpi_table_attr {
struct bin_attribute attr;
@@ -135,11 +135,9 @@ static int acpi_system_sysfs_init(void)
int table_index = 0;
int result;
- tables_kobj.parent = &acpi_subsys.kobj;
- kobject_set_name(&tables_kobj, "tables");
- result = kobject_register(&tables_kobj);
- if (result)
- return result;
+ tables_kobj = kobject_create_and_add("tables", acpi_kobj);
+ if (!tables_kobj)
+ return -ENOMEM;
do {
result = acpi_get_table_by_index(table_index, &table_header);
@@ -153,7 +151,7 @@ static int acpi_system_sysfs_init(void)
acpi_table_attr_init(table_attr, table_header);
result =
- sysfs_create_bin_file(&tables_kobj,
+ sysfs_create_bin_file(tables_kobj,
&table_attr->attr);
if (result) {
kfree(table_attr);
@@ -163,6 +161,7 @@ static int acpi_system_sysfs_init(void)
&acpi_table_attr_list);
}
} while (!result);
+ kobject_uevent(tables_kobj, KOBJ_ADD);
return 0;
}
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index b39ea3f59c9b..63e09c015ca0 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -11,6 +11,9 @@ obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
obj-$(CONFIG_SMP) += topology.o
+ifeq ($(CONFIG_SYSFS),y)
+obj-$(CONFIG_MODULES) += module.o
+endif
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
ifeq ($(CONFIG_DEBUG_DRIVER),y)
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 7370d7cf5988..d4dfb97de3b0 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -61,7 +61,7 @@ attribute_container_classdev_to_container(struct class_device *classdev)
}
EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
-static struct list_head attribute_container_list;
+static LIST_HEAD(attribute_container_list);
static DEFINE_MUTEX(attribute_container_mutex);
@@ -429,10 +429,3 @@ attribute_container_find_class_device(struct attribute_container *cont,
return cdev;
}
EXPORT_SYMBOL_GPL(attribute_container_find_class_device);
-
-int __init
-attribute_container_init(void)
-{
- INIT_LIST_HEAD(&attribute_container_list);
- return 0;
-}
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 10b2fb6c9ce6..c0444146c09a 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -1,6 +1,42 @@
-/* initialisation functions */
+/**
+ * struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure.
+ *
+ * @subsys - the struct kset that defines this bus. This is the main kobject
+ * @drivers_kset - the list of drivers associated with this bus
+ * @devices_kset - the list of devices associated with this bus
+ * @klist_devices - the klist to iterate over the @devices_kset
+ * @klist_drivers - the klist to iterate over the @drivers_kset
+ * @bus_notifier - the bus notifier list for anything that cares about things
+ * on this bus.
+ * @bus - pointer back to the struct bus_type that this structure is associated
+ * with.
+ *
+ * This structure is the one that is the actual kobject allowing struct
+ * bus_type to be statically allocated safely. Nothing outside of the driver
+ * core should ever touch these fields.
+ */
+struct bus_type_private {
+ struct kset subsys;
+ struct kset *drivers_kset;
+ struct kset *devices_kset;
+ struct klist klist_devices;
+ struct klist klist_drivers;
+ struct blocking_notifier_head bus_notifier;
+ unsigned int drivers_autoprobe:1;
+ struct bus_type *bus;
+};
+
+struct driver_private {
+ struct kobject kobj;
+ struct klist klist_devices;
+ struct klist_node knode_bus;
+ struct module_kobject *mkobj;
+ struct device_driver *driver;
+};
+#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+/* initialisation functions */
extern int devices_init(void);
extern int buses_init(void);
extern int classes_init(void);
@@ -13,17 +49,16 @@ static inline int hypervisor_init(void) { return 0; }
extern int platform_bus_init(void);
extern int system_bus_init(void);
extern int cpu_dev_init(void);
-extern int attribute_container_init(void);
-extern int bus_add_device(struct device * dev);
-extern void bus_attach_device(struct device * dev);
-extern void bus_remove_device(struct device * dev);
+extern int bus_add_device(struct device *dev);
+extern void bus_attach_device(struct device *dev);
+extern void bus_remove_device(struct device *dev);
-extern int bus_add_driver(struct device_driver *);
-extern void bus_remove_driver(struct device_driver *);
+extern int bus_add_driver(struct device_driver *drv);
+extern void bus_remove_driver(struct device_driver *drv);
-extern void driver_detach(struct device_driver * drv);
-extern int driver_probe_device(struct device_driver *, struct device *);
+extern void driver_detach(struct device_driver *drv);
+extern int driver_probe_device(struct device_driver *drv, struct device *dev);
extern void sysdev_shutdown(void);
extern int sysdev_suspend(pm_message_t state);
@@ -44,4 +79,13 @@ extern char *make_class_name(const char *name, struct kobject *kobj);
extern int devres_release_all(struct device *dev);
-extern struct kset devices_subsys;
+extern struct kset *devices_kset;
+
+#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
+extern void module_add_driver(struct module *mod, struct device_driver *drv);
+extern void module_remove_driver(struct device_driver *drv);
+#else
+static inline void module_add_driver(struct module *mod,
+ struct device_driver *drv) { }
+static inline void module_remove_driver(struct device_driver *drv) { }
+#endif
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 9a19b071c573..f484495b2ad1 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -3,6 +3,8 @@
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
*
* This file is released under the GPLv2
*
@@ -17,14 +19,13 @@
#include "power/power.h"
#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
-#define to_bus(obj) container_of(obj, struct bus_type, subsys.kobj)
+#define to_bus(obj) container_of(obj, struct bus_type_private, subsys.kobj)
/*
* sysfs bindings for drivers
*/
#define to_drv_attr(_attr) container_of(_attr, struct driver_attribute, attr)
-#define to_driver(obj) container_of(obj, struct device_driver, kobj)
static int __must_check bus_rescan_devices_helper(struct device *dev,
@@ -32,37 +33,40 @@ static int __must_check bus_rescan_devices_helper(struct device *dev,
static struct bus_type *bus_get(struct bus_type *bus)
{
- return bus ? container_of(kset_get(&bus->subsys),
- struct bus_type, subsys) : NULL;
+ if (bus) {
+ kset_get(&bus->p->subsys);
+ return bus;
+ }
+ return NULL;
}
static void bus_put(struct bus_type *bus)
{
- kset_put(&bus->subsys);
+ if (bus)
+ kset_put(&bus->p->subsys);
}
-static ssize_t
-drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
+static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
{
- struct driver_attribute * drv_attr = to_drv_attr(attr);
- struct device_driver * drv = to_driver(kobj);
+ struct driver_attribute *drv_attr = to_drv_attr(attr);
+ struct driver_private *drv_priv = to_driver(kobj);
ssize_t ret = -EIO;
if (drv_attr->show)
- ret = drv_attr->show(drv, buf);
+ ret = drv_attr->show(drv_priv->driver, buf);
return ret;
}
-static ssize_t
-drv_attr_store(struct kobject * kobj, struct attribute * attr,
- const char * buf, size_t count)
+static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
{
- struct driver_attribute * drv_attr = to_drv_attr(attr);
- struct device_driver * drv = to_driver(kobj);
+ struct driver_attribute *drv_attr = to_drv_attr(attr);
+ struct driver_private *drv_priv = to_driver(kobj);
ssize_t ret = -EIO;
if (drv_attr->store)
- ret = drv_attr->store(drv, buf, count);
+ ret = drv_attr->store(drv_priv->driver, buf, count);
return ret;
}
@@ -71,22 +75,12 @@ static struct sysfs_ops driver_sysfs_ops = {
.store = drv_attr_store,
};
-
-static void driver_release(struct kobject * kobj)
+static void driver_release(struct kobject *kobj)
{
- /*
- * Yes this is an empty release function, it is this way because struct
- * device is always a static object, not a dynamic one. Yes, this is
- * not nice and bad, but remember, drivers are code, reference counted
- * by the module count, not a device, which is really data. And yes,
- * in the future I do want to have all drivers be created dynamically,
- * and am working toward that goal, but it will take a bit longer...
- *
- * But do not let this example give _anyone_ the idea that they can
- * create a release function without any code in it at all, to do that
- * is almost always wrong. If you have any questions about this,
- * please send an email to <greg@kroah.com>
- */
+ struct driver_private *drv_priv = to_driver(kobj);
+
+ pr_debug("driver: '%s': %s\n", kobject_name(kobj), __FUNCTION__);
+ kfree(drv_priv);
}
static struct kobj_type driver_ktype = {
@@ -94,34 +88,30 @@ static struct kobj_type driver_ktype = {
.release = driver_release,
};
-
/*
* sysfs bindings for buses
*/
-
-
-static ssize_t
-bus_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
+static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
{
- struct bus_attribute * bus_attr = to_bus_attr(attr);
- struct bus_type * bus = to_bus(kobj);
+ struct bus_attribute *bus_attr = to_bus_attr(attr);
+ struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->show)
- ret = bus_attr->show(bus, buf);
+ ret = bus_attr->show(bus_priv->bus, buf);
return ret;
}
-static ssize_t
-bus_attr_store(struct kobject * kobj, struct attribute * attr,
- const char * buf, size_t count)
+static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
{
- struct bus_attribute * bus_attr = to_bus_attr(attr);
- struct bus_type * bus = to_bus(kobj);
+ struct bus_attribute *bus_attr = to_bus_attr(attr);
+ struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->store)
- ret = bus_attr->store(bus, buf, count);
+ ret = bus_attr->store(bus_priv->bus, buf, count);
return ret;
}
@@ -130,24 +120,26 @@ static struct sysfs_ops bus_sysfs_ops = {
.store = bus_attr_store,
};
-int bus_create_file(struct bus_type * bus, struct bus_attribute * attr)
+int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
int error;
if (bus_get(bus)) {
- error = sysfs_create_file(&bus->subsys.kobj, &attr->attr);
+ error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
+EXPORT_SYMBOL_GPL(bus_create_file);
-void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr)
+void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
{
if (bus_get(bus)) {
- sysfs_remove_file(&bus->subsys.kobj, &attr->attr);
+ sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
}
}
+EXPORT_SYMBOL_GPL(bus_remove_file);
static struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
@@ -166,7 +158,7 @@ static struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
-static decl_subsys(bus, &bus_ktype, &bus_uevent_ops);
+static struct kset *bus_kset;
#ifdef CONFIG_HOTPLUG
@@ -224,10 +216,13 @@ static ssize_t driver_bind(struct device_driver *drv,
if (dev->parent)
up(&dev->parent->sem);
- if (err > 0) /* success */
+ if (err > 0) {
+ /* success */
err = count;
- else if (err == 0) /* driver didn't accept device */
+ } else if (err == 0) {
+ /* driver didn't accept device */
err = -ENODEV;
+ }
}
put_device(dev);
bus_put(bus);
@@ -237,16 +232,16 @@ static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
- return sprintf(buf, "%d\n", bus->drivers_autoprobe);
+ return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
}
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
const char *buf, size_t count)
{
if (buf[0] == '0')
- bus->drivers_autoprobe = 0;
+ bus->p->drivers_autoprobe = 0;
else
- bus->drivers_autoprobe = 1;
+ bus->p->drivers_autoprobe = 1;
return count;
}
@@ -264,49 +259,49 @@ static ssize_t store_drivers_probe(struct bus_type *bus,
}
#endif
-static struct device * next_device(struct klist_iter * i)
+static struct device *next_device(struct klist_iter *i)
{
- struct klist_node * n = klist_next(i);
+ struct klist_node *n = klist_next(i);
return n ? container_of(n, struct device, knode_bus) : NULL;
}
/**
- * bus_for_each_dev - device iterator.
- * @bus: bus type.
- * @start: device to start iterating from.
- * @data: data for the callback.
- * @fn: function to be called for each device.
+ * bus_for_each_dev - device iterator.
+ * @bus: bus type.
+ * @start: device to start iterating from.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
*
- * Iterate over @bus's list of devices, and call @fn for each,
- * passing it @data. If @start is not NULL, we use that device to
- * begin iterating from.
+ * Iterate over @bus's list of devices, and call @fn for each,
+ * passing it @data. If @start is not NULL, we use that device to
+ * begin iterating from.
*
- * We check the return of @fn each time. If it returns anything
- * other than 0, we break out and return that value.
+ * We check the return of @fn each time. If it returns anything
+ * other than 0, we break out and return that value.
*
- * NOTE: The device that returns a non-zero value is not retained
- * in any way, nor is its refcount incremented. If the caller needs
- * to retain this data, it should do, and increment the reference
- * count in the supplied callback.
+ * NOTE: The device that returns a non-zero value is not retained
+ * in any way, nor is its refcount incremented. If the caller needs
+ * to retain this data, it should do, and increment the reference
+ * count in the supplied callback.
*/
-
-int bus_for_each_dev(struct bus_type * bus, struct device * start,
- void * data, int (*fn)(struct device *, void *))
+int bus_for_each_dev(struct bus_type *bus, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
- struct device * dev;
+ struct device *dev;
int error = 0;
if (!bus)
return -EINVAL;
- klist_iter_init_node(&bus->klist_devices, &i,
+ klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
+EXPORT_SYMBOL_GPL(bus_for_each_dev);
/**
* bus_find_device - device iterator for locating a particular device.
@@ -323,9 +318,9 @@ int bus_for_each_dev(struct bus_type * bus, struct device * start,
* if it does. If the callback returns non-zero, this function will
* return to the caller and not iterate over any more devices.
*/
-struct device * bus_find_device(struct bus_type *bus,
- struct device *start, void *data,
- int (*match)(struct device *, void *))
+struct device *bus_find_device(struct bus_type *bus,
+ struct device *start, void *data,
+ int (*match)(struct device *dev, void *data))
{
struct klist_iter i;
struct device *dev;
@@ -333,7 +328,7 @@ struct device * bus_find_device(struct bus_type *bus,
if (!bus)
return NULL;
- klist_iter_init_node(&bus->klist_devices, &i,
+ klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->knode_bus : NULL));
while ((dev = next_device(&i)))
if (match(dev, data) && get_device(dev))
@@ -341,51 +336,57 @@ struct device * bus_find_device(struct bus_type *bus,
klist_iter_exit(&i);
return dev;
}
+EXPORT_SYMBOL_GPL(bus_find_device);
-
-static struct device_driver * next_driver(struct klist_iter * i)
+static struct device_driver *next_driver(struct klist_iter *i)
{
- struct klist_node * n = klist_next(i);
- return n ? container_of(n, struct device_driver, knode_bus) : NULL;
+ struct klist_node *n = klist_next(i);
+ struct driver_private *drv_priv;
+
+ if (n) {
+ drv_priv = container_of(n, struct driver_private, knode_bus);
+ return drv_priv->driver;
+ }
+ return NULL;
}
/**
- * bus_for_each_drv - driver iterator
- * @bus: bus we're dealing with.
- * @start: driver to start iterating on.
- * @data: data to pass to the callback.
- * @fn: function to call for each driver.
+ * bus_for_each_drv - driver iterator
+ * @bus: bus we're dealing with.
+ * @start: driver to start iterating on.
+ * @data: data to pass to the callback.
+ * @fn: function to call for each driver.
*
- * This is nearly identical to the device iterator above.
- * We iterate over each driver that belongs to @bus, and call
- * @fn for each. If @fn returns anything but 0, we break out
- * and return it. If @start is not NULL, we use it as the head
- * of the list.
+ * This is nearly identical to the device iterator above.
+ * We iterate over each driver that belongs to @bus, and call
+ * @fn for each. If @fn returns anything but 0, we break out
+ * and return it. If @start is not NULL, we use it as the head
+ * of the list.
*
- * NOTE: we don't return the driver that returns a non-zero
- * value, nor do we leave the reference count incremented for that
- * driver. If the caller needs to know that info, it must set it
- * in the callback. It must also be sure to increment the refcount
- * so it doesn't disappear before returning to the caller.
+ * NOTE: we don't return the driver that returns a non-zero
+ * value, nor do we leave the reference count incremented for that
+ * driver. If the caller needs to know that info, it must set it
+ * in the callback. It must also be sure to increment the refcount
+ * so it doesn't disappear before returning to the caller.
*/
-
-int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
- void * data, int (*fn)(struct device_driver *, void *))
+int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
+ void *data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
- struct device_driver * drv;
+ struct device_driver *drv;
int error = 0;
if (!bus)
return -EINVAL;
- klist_iter_init_node(&bus->klist_drivers, &i,
- start ? &start->knode_bus : NULL);
+ klist_iter_init_node(&bus->p->klist_drivers, &i,
+ start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
+EXPORT_SYMBOL_GPL(bus_for_each_drv);
static int device_add_attrs(struct bus_type *bus, struct device *dev)
{
@@ -396,7 +397,7 @@ static int device_add_attrs(struct bus_type *bus, struct device *dev)
return 0;
for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
- error = device_create_file(dev,&bus->dev_attrs[i]);
+ error = device_create_file(dev, &bus->dev_attrs[i]);
if (error) {
while (--i >= 0)
device_remove_file(dev, &bus->dev_attrs[i]);
@@ -406,13 +407,13 @@ static int device_add_attrs(struct bus_type *bus, struct device *dev)
return error;
}
-static void device_remove_attrs(struct bus_type * bus, struct device * dev)
+static void device_remove_attrs(struct bus_type *bus, struct device *dev)
{
int i;
if (bus->dev_attrs) {
for (i = 0; attr_name(bus->dev_attrs[i]); i++)
- device_remove_file(dev,&bus->dev_attrs[i]);
+ device_remove_file(dev, &bus->dev_attrs[i]);
}
}
@@ -420,7 +421,7 @@ static void device_remove_attrs(struct bus_type * bus, struct device * dev)
static int make_deprecated_bus_links(struct device *dev)
{
return sysfs_create_link(&dev->kobj,
- &dev->bus->subsys.kobj, "bus");
+ &dev->bus->p->subsys.kobj, "bus");
}
static void remove_deprecated_bus_links(struct device *dev)
@@ -433,28 +434,28 @@ static inline void remove_deprecated_bus_links(struct device *dev) { }
#endif
/**
- * bus_add_device - add device to bus
- * @dev: device being added
+ * bus_add_device - add device to bus
+ * @dev: device being added
*
- * - Add the device to its bus's list of devices.
- * - Create link to device's bus.
+ * - Add the device to its bus's list of devices.
+ * - Create link to device's bus.
*/
-int bus_add_device(struct device * dev)
+int bus_add_device(struct device *dev)
{
- struct bus_type * bus = bus_get(dev->bus);
+ struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
- pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
+ pr_debug("bus: '%s': add device %s\n", bus->name, dev->bus_id);
error = device_add_attrs(bus, dev);
if (error)
goto out_put;
- error = sysfs_create_link(&bus->devices.kobj,
+ error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev->bus_id);
if (error)
goto out_id;
error = sysfs_create_link(&dev->kobj,
- &dev->bus->subsys.kobj, "subsystem");
+ &dev->bus->p->subsys.kobj, "subsystem");
if (error)
goto out_subsys;
error = make_deprecated_bus_links(dev);
@@ -466,7 +467,7 @@ int bus_add_device(struct device * dev)
out_deprecated:
sysfs_remove_link(&dev->kobj, "subsystem");
out_subsys:
- sysfs_remove_link(&bus->devices.kobj, dev->bus_id);
+ sysfs_remove_link(&bus->p->devices_kset->kobj, dev->bus_id);
out_id:
device_remove_attrs(bus, dev);
out_put:
@@ -475,56 +476,58 @@ out_put:
}
/**
- * bus_attach_device - add device to bus
- * @dev: device tried to attach to a driver
+ * bus_attach_device - add device to bus
+ * @dev: device tried to attach to a driver
*
- * - Add device to bus's list of devices.
- * - Try to attach to driver.
+ * - Add device to bus's list of devices.
+ * - Try to attach to driver.
*/
-void bus_attach_device(struct device * dev)
+void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) {
dev->is_registered = 1;
- if (bus->drivers_autoprobe)
+ if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
WARN_ON(ret < 0);
if (ret >= 0)
- klist_add_tail(&dev->knode_bus, &bus->klist_devices);
+ klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
else
dev->is_registered = 0;
}
}
/**
- * bus_remove_device - remove device from bus
- * @dev: device to be removed
+ * bus_remove_device - remove device from bus
+ * @dev: device to be removed
*
- * - Remove symlink from bus's directory.
- * - Delete device from bus's list.
- * - Detach from its driver.
- * - Drop reference taken in bus_add_device().
+ * - Remove symlink from bus's directory.
+ * - Delete device from bus's list.
+ * - Detach from its driver.
+ * - Drop reference taken in bus_add_device().
*/
-void bus_remove_device(struct device * dev)
+void bus_remove_device(struct device *dev)
{
if (dev->bus) {
sysfs_remove_link(&dev->kobj, "subsystem");
remove_deprecated_bus_links(dev);
- sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
+ dev->bus_id);
device_remove_attrs(dev->bus, dev);
if (dev->is_registered) {
dev->is_registered = 0;
klist_del(&dev->knode_bus);
}
- pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id);
+ pr_debug("bus: '%s': remove device %s\n",
+ dev->bus->name, dev->bus_id);
device_release_driver(dev);
bus_put(dev->bus);
}
}
-static int driver_add_attrs(struct bus_type * bus, struct device_driver * drv)
+static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv)
{
int error = 0;
int i;
@@ -533,19 +536,19 @@ static int driver_add_attrs(struct bus_type * bus, struct device_driver * drv)
for (i = 0; attr_name(bus->drv_attrs[i]); i++) {
error = driver_create_file(drv, &bus->drv_attrs[i]);
if (error)
- goto Err;
+ goto err;
}
}
- Done:
+done:
return error;
- Err:
+err:
while (--i >= 0)
driver_remove_file(drv, &bus->drv_attrs[i]);
- goto Done;
+ goto done;
}
-
-static void driver_remove_attrs(struct bus_type * bus, struct device_driver * drv)
+static void driver_remove_attrs(struct bus_type *bus,
+ struct device_driver *drv)
{
int i;
@@ -616,39 +619,46 @@ static ssize_t driver_uevent_store(struct device_driver *drv,
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0)
- kobject_uevent(&drv->kobj, action);
+ kobject_uevent(&drv->p->kobj, action);
return count;
}
static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store);
/**
- * bus_add_driver - Add a driver to the bus.
- * @drv: driver.
- *
+ * bus_add_driver - Add a driver to the bus.
+ * @drv: driver.
*/
int bus_add_driver(struct device_driver *drv)
{
- struct bus_type * bus = bus_get(drv->bus);
+ struct bus_type *bus;
+ struct driver_private *priv;
int error = 0;
+ bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
- pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
- error = kobject_set_name(&drv->kobj, "%s", drv->name);
- if (error)
- goto out_put_bus;
- drv->kobj.kset = &bus->drivers;
- error = kobject_register(&drv->kobj);
+ pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ klist_init(&priv->klist_devices, NULL, NULL);
+ priv->driver = drv;
+ drv->p = priv;
+ priv->kobj.kset = bus->p->drivers_kset;
+ error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
+ "%s", drv->name);
if (error)
goto out_put_bus;
- if (drv->bus->drivers_autoprobe) {
+ if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
- klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
+ klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
@@ -669,24 +679,24 @@ int bus_add_driver(struct device_driver *drv)
__FUNCTION__, drv->name);
}
+ kobject_uevent(&priv->kobj, KOBJ_ADD);
return error;
out_unregister:
- kobject_unregister(&drv->kobj);
+ kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
/**
- * bus_remove_driver - delete driver from bus's knowledge.
- * @drv: driver.
+ * bus_remove_driver - delete driver from bus's knowledge.
+ * @drv: driver.
*
- * Detach the driver from the devices it controls, and remove
- * it from its bus's list of drivers. Finally, we drop the reference
- * to the bus we took in bus_add_driver().
+ * Detach the driver from the devices it controls, and remove
+ * it from its bus's list of drivers. Finally, we drop the reference
+ * to the bus we took in bus_add_driver().
*/
-
-void bus_remove_driver(struct device_driver * drv)
+void bus_remove_driver(struct device_driver *drv)
{
if (!drv->bus)
return;
@@ -694,18 +704,17 @@ void bus_remove_driver(struct device_driver * drv)
remove_bind_files(drv);
driver_remove_attrs(drv->bus, drv);
driver_remove_file(drv, &driver_attr_uevent);
- klist_remove(&drv->knode_bus);
- pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name);
+ klist_remove(&drv->p->knode_bus);
+ pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name);
driver_detach(drv);
module_remove_driver(drv);
- kobject_unregister(&drv->kobj);
+ kobject_put(&drv->p->kobj);
bus_put(drv->bus);
}
-
/* Helper for bus_rescan_devices's iter */
static int __must_check bus_rescan_devices_helper(struct device *dev,
- void *data)
+ void *data)
{
int ret = 0;
@@ -727,10 +736,11 @@ static int __must_check bus_rescan_devices_helper(struct device *dev,
* attached and rescan it against existing drivers to see if it matches
* any by calling device_attach() for the unbound devices.
*/
-int bus_rescan_devices(struct bus_type * bus)
+int bus_rescan_devices(struct bus_type *bus)
{
return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
}
+EXPORT_SYMBOL_GPL(bus_rescan_devices);
/**
* device_reprobe - remove driver for a device and probe for a new driver
@@ -755,55 +765,55 @@ int device_reprobe(struct device *dev)
EXPORT_SYMBOL_GPL(device_reprobe);
/**
- * find_bus - locate bus by name.
- * @name: name of bus.
+ * find_bus - locate bus by name.
+ * @name: name of bus.
*
- * Call kset_find_obj() to iterate over list of buses to
- * find a bus by name. Return bus if found.
+ * Call kset_find_obj() to iterate over list of buses to
+ * find a bus by name. Return bus if found.
*
- * Note that kset_find_obj increments bus' reference count.
+ * Note that kset_find_obj increments bus' reference count.
*/
#if 0
-struct bus_type * find_bus(char * name)
+struct bus_type *find_bus(char *name)
{
- struct kobject * k = kset_find_obj(&bus_subsys.kset, name);
+ struct kobject *k = kset_find_obj(bus_kset, name);
return k ? to_bus(k) : NULL;
}
#endif /* 0 */
/**
- * bus_add_attrs - Add default attributes for this bus.
- * @bus: Bus that has just been registered.
+ * bus_add_attrs - Add default attributes for this bus.
+ * @bus: Bus that has just been registered.
*/
-static int bus_add_attrs(struct bus_type * bus)
+static int bus_add_attrs(struct bus_type *bus)
{
int error = 0;
int i;
if (bus->bus_attrs) {
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
- error = bus_create_file(bus,&bus->bus_attrs[i]);
+ error = bus_create_file(bus, &bus->bus_attrs[i]);
if (error)
- goto Err;
+ goto err;
}
}
- Done:
+done:
return error;
- Err:
+err:
while (--i >= 0)
- bus_remove_file(bus,&bus->bus_attrs[i]);
- goto Done;
+ bus_remove_file(bus, &bus->bus_attrs[i]);
+ goto done;
}
-static void bus_remove_attrs(struct bus_type * bus)
+static void bus_remove_attrs(struct bus_type *bus)
{
int i;
if (bus->bus_attrs) {
for (i = 0; attr_name(bus->bus_attrs[i]); i++)
- bus_remove_file(bus,&bus->bus_attrs[i]);
+ bus_remove_file(bus, &bus->bus_attrs[i]);
}
}
@@ -827,32 +837,42 @@ static ssize_t bus_uevent_store(struct bus_type *bus,
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0)
- kobject_uevent(&bus->subsys.kobj, action);
+ kobject_uevent(&bus->p->subsys.kobj, action);
return count;
}
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
/**
- * bus_register - register a bus with the system.
- * @bus: bus.
+ * bus_register - register a bus with the system.
+ * @bus: bus.
*
- * Once we have that, we registered the bus with the kobject
- * infrastructure, then register the children subsystems it has:
- * the devices and drivers that belong to the bus.
+ * Once we have that, we registered the bus with the kobject
+ * infrastructure, then register the children subsystems it has:
+ * the devices and drivers that belong to the bus.
*/
-int bus_register(struct bus_type * bus)
+int bus_register(struct bus_type *bus)
{
int retval;
+ struct bus_type_private *priv;
+
+ priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);
+ priv->bus = bus;
+ bus->p = priv;
- retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);
+ BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
+
+ retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
- bus->subsys.kobj.kset = &bus_subsys;
+ priv->subsys.kobj.kset = bus_kset;
+ priv->subsys.kobj.ktype = &bus_ktype;
+ priv->drivers_autoprobe = 1;
- retval = subsystem_register(&bus->subsys);
+ retval = kset_register(&priv->subsys);
if (retval)
goto out;
@@ -860,23 +880,23 @@ int bus_register(struct bus_type * bus)
if (retval)
goto bus_uevent_fail;
- kobject_set_name(&bus->devices.kobj, "devices");
- bus->devices.kobj.parent = &bus->subsys.kobj;
- retval = kset_register(&bus->devices);
- if (retval)
+ priv->devices_kset = kset_create_and_add("devices", NULL,
+ &priv->subsys.kobj);
+ if (!priv->devices_kset) {
+ retval = -ENOMEM;
goto bus_devices_fail;
+ }
- kobject_set_name(&bus->drivers.kobj, "drivers");
- bus->drivers.kobj.parent = &bus->subsys.kobj;
- bus->drivers.ktype = &driver_ktype;
- retval = kset_register(&bus->drivers);
- if (retval)
+ priv->drivers_kset = kset_create_and_add("drivers", NULL,
+ &priv->subsys.kobj);
+ if (!priv->drivers_kset) {
+ retval = -ENOMEM;
goto bus_drivers_fail;
+ }
- klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
- klist_init(&bus->klist_drivers, NULL, NULL);
+ klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
+ klist_init(&priv->klist_drivers, NULL, NULL);
- bus->drivers_autoprobe = 1;
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
@@ -885,66 +905,73 @@ int bus_register(struct bus_type * bus)
if (retval)
goto bus_attrs_fail;
- pr_debug("bus type '%s' registered\n", bus->name);
+ pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
- kset_unregister(&bus->drivers);
+ kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
- kset_unregister(&bus->devices);
+ kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
- subsystem_unregister(&bus->subsys);
+ kset_unregister(&bus->p->subsys);
+ kfree(bus->p);
out:
return retval;
}
+EXPORT_SYMBOL_GPL(bus_register);
/**
- * bus_unregister - remove a bus from the system
- * @bus: bus.
+ * bus_unregister - remove a bus from the system
+ * @bus: bus.
*
- * Unregister the child subsystems and the bus itself.
- * Finally, we call bus_put() to release the refcount
+ * Unregister the child subsystems and the bus itself.
+ * Finally, we call bus_put() to release the refcount
*/
-void bus_unregister(struct bus_type * bus)
+void bus_unregister(struct bus_type *bus)
{
- pr_debug("bus %s: unregistering\n", bus->name);
+ pr_debug("bus: '%s': unregistering\n", bus->name);
bus_remove_attrs(bus);
remove_probe_files(bus);
- kset_unregister(&bus->drivers);
- kset_unregister(&bus->devices);
+ kset_unregister(bus->p->drivers_kset);
+ kset_unregister(bus->p->devices_kset);
bus_remove_file(bus, &bus_attr_uevent);
- subsystem_unregister(&bus->subsys);
+ kset_unregister(&bus->p->subsys);
+ kfree(bus->p);
}
+EXPORT_SYMBOL_GPL(bus_unregister);
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
- return blocking_notifier_chain_register(&bus->bus_notifier, nb);
+ return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}
EXPORT_SYMBOL_GPL(bus_register_notifier);
int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
{
- return blocking_notifier_chain_unregister(&bus->bus_notifier, nb);
+ return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb);
}
EXPORT_SYMBOL_GPL(bus_unregister_notifier);
-int __init buses_init(void)
+struct kset *bus_get_kset(struct bus_type *bus)
{
- return subsystem_register(&bus_subsys);
+ return &bus->p->subsys;
}
+EXPORT_SYMBOL_GPL(bus_get_kset);
+struct klist *bus_get_device_klist(struct bus_type *bus)
+{
+ return &bus->p->klist_devices;
+}
+EXPORT_SYMBOL_GPL(bus_get_device_klist);
-EXPORT_SYMBOL_GPL(bus_for_each_dev);
-EXPORT_SYMBOL_GPL(bus_find_device);
-EXPORT_SYMBOL_GPL(bus_for_each_drv);
-
-EXPORT_SYMBOL_GPL(bus_register);
-EXPORT_SYMBOL_GPL(bus_unregister);
-EXPORT_SYMBOL_GPL(bus_rescan_devices);
-
-EXPORT_SYMBOL_GPL(bus_create_file);
-EXPORT_SYMBOL_GPL(bus_remove_file);
+int __init buses_init(void)
+{
+ bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
+ if (!bus_kset)
+ return -ENOMEM;
+ return 0;
+}
diff --git a/drivers/base/class.c b/drivers/base/class.c
index a863bb091e11..59cf35894cfc 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -17,16 +17,17 @@
#include <linux/kdev_t.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/genhd.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.kobj)
-static ssize_t
-class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
+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);
+ struct class_attribute *class_attr = to_class_attr(attr);
+ struct class *dc = to_class(kobj);
ssize_t ret = -EIO;
if (class_attr->show)
@@ -34,12 +35,11 @@ class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
return ret;
}
-static ssize_t
-class_attr_store(struct kobject * kobj, struct attribute * attr,
- const char * buf, size_t count)
+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);
+ struct class_attribute *class_attr = to_class_attr(attr);
+ struct class *dc = to_class(kobj);
ssize_t ret = -EIO;
if (class_attr->store)
@@ -47,7 +47,7 @@ class_attr_store(struct kobject * kobj, struct attribute * attr,
return ret;
}
-static void class_release(struct kobject * kobj)
+static void class_release(struct kobject *kobj)
{
struct class *class = to_class(kobj);
@@ -71,20 +71,20 @@ static struct kobj_type class_ktype = {
};
/* Hotplug events for classes go to the class_obj subsys */
-static decl_subsys(class, &class_ktype, NULL);
+static struct kset *class_kset;
-int class_create_file(struct class * cls, const struct class_attribute * attr)
+int class_create_file(struct class *cls, const struct class_attribute *attr)
{
int error;
- if (cls) {
+ if (cls)
error = sysfs_create_file(&cls->subsys.kobj, &attr->attr);
- } else
+ else
error = -EINVAL;
return error;
}
-void class_remove_file(struct class * cls, const struct class_attribute * attr)
+void class_remove_file(struct class *cls, const struct class_attribute *attr)
{
if (cls)
sysfs_remove_file(&cls->subsys.kobj, &attr->attr);
@@ -93,48 +93,48 @@ void class_remove_file(struct class * cls, const struct class_attribute * attr)
static struct class *class_get(struct class *cls)
{
if (cls)
- return container_of(kset_get(&cls->subsys), struct class, subsys);
+ return container_of(kset_get(&cls->subsys),
+ struct class, subsys);
return NULL;
}
-static void class_put(struct class * cls)
+static void class_put(struct class *cls)
{
if (cls)
kset_put(&cls->subsys);
}
-
-static int add_class_attrs(struct class * cls)
+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]);
+ error = class_create_file(cls, &cls->class_attrs[i]);
if (error)
- goto Err;
+ goto error;
}
}
- Done:
+done:
return error;
- Err:
+error:
while (--i >= 0)
- class_remove_file(cls,&cls->class_attrs[i]);
- goto Done;
+ class_remove_file(cls, &cls->class_attrs[i]);
+ goto done;
}
-static void remove_class_attrs(struct class * cls)
+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]);
+ class_remove_file(cls, &cls->class_attrs[i]);
}
}
-int class_register(struct class * cls)
+int class_register(struct class *cls)
{
int error;
@@ -149,9 +149,16 @@ int class_register(struct class * cls)
if (error)
return error;
- cls->subsys.kobj.kset = &class_subsys;
+#ifdef CONFIG_SYSFS_DEPRECATED
+ /* let the block class directory show up in the root of sysfs */
+ if (cls != &block_class)
+ cls->subsys.kobj.kset = class_kset;
+#else
+ cls->subsys.kobj.kset = class_kset;
+#endif
+ cls->subsys.kobj.ktype = &class_ktype;
- error = subsystem_register(&cls->subsys);
+ error = kset_register(&cls->subsys);
if (!error) {
error = add_class_attrs(class_get(cls));
class_put(cls);
@@ -159,11 +166,11 @@ int class_register(struct class * cls)
return error;
}
-void class_unregister(struct class * cls)
+void class_unregister(struct class *cls)
{
pr_debug("device class '%s': unregistering\n", cls->name);
remove_class_attrs(cls);
- subsystem_unregister(&cls->subsys);
+ kset_unregister(&cls->subsys);
}
static void class_create_release(struct class *cls)
@@ -241,8 +248,8 @@ void class_destroy(struct class *cls)
/* Class Device Stuff */
-int class_device_create_file(struct class_device * class_dev,
- const struct class_device_attribute * attr)
+int class_device_create_file(struct class_device *class_dev,
+ const struct class_device_attribute *attr)
{
int error = -EINVAL;
if (class_dev)
@@ -250,8 +257,8 @@ int class_device_create_file(struct class_device * class_dev,
return error;
}
-void class_device_remove_file(struct class_device * class_dev,
- const struct class_device_attribute * attr)
+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);
@@ -273,12 +280,11 @@ void class_device_remove_bin_file(struct class_device *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)
+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);
+ 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)
@@ -286,12 +292,12 @@ class_device_attr_show(struct kobject * kobj, struct attribute * attr,
return ret;
}
-static ssize_t
-class_device_attr_store(struct kobject * kobj, struct attribute * attr,
- const char * buf, size_t count)
+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);
+ 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)
@@ -304,10 +310,10 @@ static struct sysfs_ops class_dev_sysfs_ops = {
.store = class_device_attr_store,
};
-static void class_dev_release(struct kobject * kobj)
+static void class_dev_release(struct kobject *kobj)
{
struct class_device *cd = to_class_dev(kobj);
- struct class * cls = cd->class;
+ struct class *cls = cd->class;
pr_debug("device class '%s': release.\n", cd->class_id);
@@ -316,8 +322,8 @@ static void class_dev_release(struct kobject * kobj)
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",
+ 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);
}
@@ -428,7 +434,8 @@ static int class_uevent(struct kset *kset, struct kobject *kobj,
add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
if (dev->driver)
- add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name);
+ add_uevent_var(env, "PHYSDEVDRIVER=%s",
+ dev->driver->name);
}
if (class_dev->uevent) {
@@ -452,43 +459,49 @@ static struct kset_uevent_ops class_uevent_ops = {
.uevent = class_uevent,
};
-static decl_subsys(class_obj, &class_device_ktype, &class_uevent_ops);
-
+/*
+ * DO NOT copy how this is created, kset_create_and_add() should be
+ * called, but this is a hold-over from the old-way and will be deleted
+ * entirely soon.
+ */
+static struct kset class_obj_subsys = {
+ .uevent_ops = &class_uevent_ops,
+};
-static int class_device_add_attrs(struct class_device * cd)
+static int class_device_add_attrs(struct class_device *cd)
{
int i;
int error = 0;
- struct class * cls = cd->class;
+ 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]);
+ &cls->class_dev_attrs[i]);
if (error)
- goto Err;
+ goto err;
}
}
- Done:
+done:
return error;
- Err:
+err:
while (--i >= 0)
- class_device_remove_file(cd,&cls->class_dev_attrs[i]);
- goto Done;
+ class_device_remove_file(cd, &cls->class_dev_attrs[i]);
+ goto done;
}
-static void class_device_remove_attrs(struct class_device * cd)
+static void class_device_remove_attrs(struct class_device *cd)
{
int i;
- struct class * cls = cd->class;
+ 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]);
+ class_device_remove_file(cd, &cls->class_dev_attrs[i]);
}
}
-static int class_device_add_groups(struct class_device * cd)
+static int class_device_add_groups(struct class_device *cd)
{
int i;
int error = 0;
@@ -498,7 +511,8 @@ static int class_device_add_groups(struct class_device * cd)
error = sysfs_create_group(&cd->kobj, cd->groups[i]);
if (error) {
while (--i >= 0)
- sysfs_remove_group(&cd->kobj, cd->groups[i]);
+ sysfs_remove_group(&cd->kobj,
+ cd->groups[i]);
goto out;
}
}
@@ -507,14 +521,12 @@ out:
return error;
}
-static void class_device_remove_groups(struct class_device * cd)
+static void class_device_remove_groups(struct class_device *cd)
{
int i;
- if (cd->groups) {
- for (i = 0; cd->groups[i]; 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)
@@ -537,8 +549,8 @@ static struct class_device_attribute class_uevent_attr =
void class_device_initialize(struct class_device *class_dev)
{
- kobj_set_kset_s(class_dev, class_obj_subsys);
- kobject_init(&class_dev->kobj);
+ class_dev->kobj.kset = &class_obj_subsys;
+ kobject_init(&class_dev->kobj, &class_device_ktype);
INIT_LIST_HEAD(&class_dev->node);
}
@@ -566,16 +578,13 @@ int class_device_add(struct class_device *class_dev)
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.kobj;
- error = kobject_add(&class_dev->kobj);
+ error = kobject_add(&class_dev->kobj, class_dev->kobj.parent,
+ "%s", class_dev->class_id);
if (error)
goto out2;
@@ -642,7 +651,7 @@ int class_device_add(struct class_device *class_dev)
out3:
kobject_del(&class_dev->kobj);
out2:
- if(parent_class_dev)
+ if (parent_class_dev)
class_device_put(parent_class_dev);
class_put(parent_class);
out1:
@@ -659,9 +668,11 @@ int class_device_register(struct class_device *class_dev)
/**
* class_device_create - creates a class device and registers it with sysfs
* @cls: 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.
+ * @parent: pointer to the parent struct class_device of this new device, if
+ * any.
* @devt: the dev_t for the char device to be added.
- * @device: a pointer to a struct device that is assiociated with this class device.
+ * @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
@@ -785,7 +796,7 @@ void class_device_destroy(struct class *cls, dev_t devt)
class_device_unregister(class_dev);
}
-struct class_device * class_device_get(struct class_device *class_dev)
+struct class_device *class_device_get(struct class_device *class_dev)
{
if (class_dev)
return to_class_dev(kobject_get(&class_dev->kobj));
@@ -798,6 +809,139 @@ void class_device_put(struct class_device *class_dev)
kobject_put(&class_dev->kobj);
}
+/**
+ * class_for_each_device - device iterator
+ * @class: the class we're iterating
+ * @data: data for the callback
+ * @fn: function to be called for each device
+ *
+ * Iterate over @class's list of devices, and call @fn for each,
+ * passing it @data.
+ *
+ * We check the return of @fn each time. If it returns anything
+ * other than 0, we break out and return that value.
+ *
+ * Note, we hold class->sem in this function, so it can not be
+ * re-acquired in @fn, otherwise it will self-deadlocking. For
+ * example, calls to add or remove class members would be verboten.
+ */
+int class_for_each_device(struct class *class, void *data,
+ int (*fn)(struct device *, void *))
+{
+ struct device *dev;
+ int error = 0;
+
+ if (!class)
+ return -EINVAL;
+ down(&class->sem);
+ list_for_each_entry(dev, &class->devices, node) {
+ dev = get_device(dev);
+ if (dev) {
+ error = fn(dev, data);
+ put_device(dev);
+ } else
+ error = -ENODEV;
+ if (error)
+ break;
+ }
+ up(&class->sem);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(class_for_each_device);
+
+/**
+ * class_find_device - device iterator for locating a particular device
+ * @class: the class we're iterating
+ * @data: data for the match function
+ * @match: function to check device
+ *
+ * This is similar to the class_for_each_dev() function above, but it
+ * returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more devices.
+
+ * Note, you will need to drop the reference with put_device() after use.
+ *
+ * We hold class->sem in this function, so it can not be
+ * re-acquired in @match, otherwise it will self-deadlocking. For
+ * example, calls to add or remove class members would be verboten.
+ */
+struct device *class_find_device(struct class *class, void *data,
+ int (*match)(struct device *, void *))
+{
+ struct device *dev;
+ int found = 0;
+
+ if (!class)
+ return NULL;
+
+ down(&class->sem);
+ list_for_each_entry(dev, &class->devices, node) {
+ dev = get_device(dev);
+ if (dev) {
+ if (match(dev, data)) {
+ found = 1;
+ break;
+ } else
+ put_device(dev);
+ } else
+ break;
+ }
+ up(&class->sem);
+
+ return found ? dev : NULL;
+}
+EXPORT_SYMBOL_GPL(class_find_device);
+
+/**
+ * class_find_child - device iterator for locating a particular class_device
+ * @class: the class we're iterating
+ * @data: data for the match function
+ * @match: function to check class_device
+ *
+ * This function returns a reference to a class_device that is 'found' for
+ * later use, as determined by the @match callback.
+ *
+ * The callback should return 0 if the class_device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more class_devices.
+ *
+ * Note, you will need to drop the reference with class_device_put() after use.
+ *
+ * We hold class->sem in this function, so it can not be
+ * re-acquired in @match, otherwise it will self-deadlocking. For
+ * example, calls to add or remove class members would be verboten.
+ */
+struct class_device *class_find_child(struct class *class, void *data,
+ int (*match)(struct class_device *, void *))
+{
+ struct class_device *dev;
+ int found = 0;
+
+ if (!class)
+ return NULL;
+
+ down(&class->sem);
+ list_for_each_entry(dev, &class->children, node) {
+ dev = class_device_get(dev);
+ if (dev) {
+ if (match(dev, data)) {
+ found = 1;
+ break;
+ } else
+ class_device_put(dev);
+ } else
+ break;
+ }
+ up(&class->sem);
+
+ return found ? dev : NULL;
+}
+EXPORT_SYMBOL_GPL(class_find_child);
int class_interface_register(struct class_interface *class_intf)
{
@@ -829,7 +973,7 @@ int class_interface_register(struct class_interface *class_intf)
void class_interface_unregister(struct class_interface *class_intf)
{
- struct class * parent = class_intf->class;
+ struct class *parent = class_intf->class;
struct class_device *class_dev;
struct device *dev;
@@ -853,15 +997,14 @@ void class_interface_unregister(struct class_interface *class_intf)
int __init classes_init(void)
{
- int retval;
-
- retval = subsystem_register(&class_subsys);
- if (retval)
- return retval;
+ class_kset = kset_create_and_add("class", NULL, NULL);
+ if (!class_kset)
+ return -ENOMEM;
/* ick, this is ugly, the things we go through to keep from showing up
* in sysfs... */
kset_init(&class_obj_subsys);
+ kobject_set_name(&class_obj_subsys.kobj, "class_obj");
if (!class_obj_subsys.kobj.parent)
class_obj_subsys.kobj.parent = &class_obj_subsys.kobj;
return 0;
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2683eac30c68..edf3bbeb8d6a 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -18,14 +18,14 @@
#include <linux/string.h>
#include <linux/kdev_t.h>
#include <linux/notifier.h>
-
+#include <linux/genhd.h>
#include <asm/semaphore.h>
#include "base.h"
#include "power/power.h"
-int (*platform_notify)(struct device * dev) = NULL;
-int (*platform_notify_remove)(struct device * dev) = NULL;
+int (*platform_notify)(struct device *dev) = NULL;
+int (*platform_notify_remove)(struct device *dev) = NULL;
/*
* sysfs bindings for devices.
@@ -51,11 +51,11 @@ EXPORT_SYMBOL(dev_driver_string);
#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
-static ssize_t
-dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
+static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
{
- struct device_attribute * dev_attr = to_dev_attr(attr);
- struct device * dev = to_dev(kobj);
+ struct device_attribute *dev_attr = to_dev_attr(attr);
+ struct device *dev = to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
@@ -63,12 +63,11 @@ dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
return ret;
}
-static ssize_t
-dev_attr_store(struct kobject * kobj, struct attribute * attr,
- const char * buf, size_t count)
+static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
{
- struct device_attribute * dev_attr = to_dev_attr(attr);
- struct device * dev = to_dev(kobj);
+ struct device_attribute *dev_attr = to_dev_attr(attr);
+ struct device *dev = to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->store)
@@ -90,9 +89,9 @@ static struct sysfs_ops dev_sysfs_ops = {
* reaches 0. We forward the call to the device's release
* method, which should handle actually freeing the structure.
*/
-static void device_release(struct kobject * kobj)
+static void device_release(struct kobject *kobj)
{
- struct device * dev = to_dev(kobj);
+ struct device *dev = to_dev(kobj);
if (dev->release)
dev->release(dev);
@@ -101,8 +100,8 @@ static void device_release(struct kobject * kobj)
else if (dev->class && dev->class->dev_release)
dev->class->dev_release(dev);
else {
- printk(KERN_ERR "Device '%s' does not have a release() function, "
- "it is broken and must be fixed.\n",
+ printk(KERN_ERR "Device '%s' does not have a release() "
+ "function, it is broken and must be fixed.\n",
dev->bus_id);
WARN_ON(1);
}
@@ -185,7 +184,8 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj,
add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
if (dev->driver)
- add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name);
+ add_uevent_var(env, "PHYSDEVDRIVER=%s",
+ dev->driver->name);
}
#endif
@@ -193,15 +193,16 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj,
if (dev->bus && dev->bus->uevent) {
retval = dev->bus->uevent(dev, env);
if (retval)
- pr_debug ("%s: bus uevent() returned %d\n",
- __FUNCTION__, retval);
+ pr_debug("device: '%s': %s: bus uevent() returned %d\n",
+ dev->bus_id, __FUNCTION__, retval);
}
/* have the class specific function add its stuff */
if (dev->class && dev->class->dev_uevent) {
retval = dev->class->dev_uevent(dev, env);
if (retval)
- pr_debug("%s: class uevent() returned %d\n",
+ pr_debug("device: '%s': %s: class uevent() "
+ "returned %d\n", dev->bus_id,
__FUNCTION__, retval);
}
@@ -209,7 +210,8 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj,
if (dev->type && dev->type->uevent) {
retval = dev->type->uevent(dev, env);
if (retval)
- pr_debug("%s: dev_type uevent() returned %d\n",
+ pr_debug("device: '%s': %s: dev_type uevent() "
+ "returned %d\n", dev->bus_id,
__FUNCTION__, retval);
}
@@ -325,7 +327,8 @@ static int device_add_groups(struct device *dev,
error = sysfs_create_group(&dev->kobj, groups[i]);
if (error) {
while (--i >= 0)
- sysfs_remove_group(&dev->kobj, groups[i]);
+ sysfs_remove_group(&dev->kobj,
+ groups[i]);
break;
}
}
@@ -401,20 +404,15 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
static struct device_attribute devt_attr =
__ATTR(dev, S_IRUGO, show_dev, NULL);
-/*
- * devices_subsys - structure to be registered with kobject core.
- */
-
-decl_subsys(devices, &device_ktype, &device_uevent_ops);
-
+/* kset to create /sys/devices/ */
+struct kset *devices_kset;
/**
- * device_create_file - create sysfs attribute file for device.
- * @dev: device.
- * @attr: device attribute descriptor.
+ * device_create_file - create sysfs attribute file for device.
+ * @dev: device.
+ * @attr: device attribute descriptor.
*/
-
-int device_create_file(struct device * dev, struct device_attribute * attr)
+int device_create_file(struct device *dev, struct device_attribute *attr)
{
int error = 0;
if (get_device(dev)) {
@@ -425,12 +423,11 @@ int device_create_file(struct device * dev, struct device_attribute * attr)
}
/**
- * device_remove_file - remove sysfs attribute file.
- * @dev: device.
- * @attr: device attribute descriptor.
+ * device_remove_file - remove sysfs attribute file.
+ * @dev: device.
+ * @attr: device attribute descriptor.
*/
-
-void device_remove_file(struct device * dev, struct device_attribute * attr)
+void device_remove_file(struct device *dev, struct device_attribute *attr)
{
if (get_device(dev)) {
sysfs_remove_file(&dev->kobj, &attr->attr);
@@ -511,22 +508,20 @@ static void klist_children_put(struct klist_node *n)
put_device(dev);
}
-
/**
- * device_initialize - init device structure.
- * @dev: device.
+ * device_initialize - init device structure.
+ * @dev: device.
*
- * This prepares the device for use by other layers,
- * including adding it to the device hierarchy.
- * It is the first half of device_register(), if called by
- * that, though it can also be called separately, so one
- * may use @dev's fields (e.g. the refcount).
+ * This prepares the device for use by other layers,
+ * including adding it to the device hierarchy.
+ * It is the first half of device_register(), if called by
+ * that, though it can also be called separately, so one
+ * may use @dev's fields (e.g. the refcount).
*/
-
void device_initialize(struct device *dev)
{
- kobj_set_kset_s(dev, devices_subsys);
- kobject_init(&dev->kobj);
+ dev->kobj.kset = devices_kset;
+ kobject_init(&dev->kobj, &device_ktype);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
@@ -539,36 +534,39 @@ void device_initialize(struct device *dev)
}
#ifdef CONFIG_SYSFS_DEPRECATED
-static struct kobject * get_device_parent(struct device *dev,
- struct device *parent)
+static struct kobject *get_device_parent(struct device *dev,
+ struct device *parent)
{
- /*
- * Set the parent to the class, not the parent device
- * for topmost devices in class hierarchy.
- * This keeps sysfs from having a symlink to make old
- * udevs happy
- */
+ /* class devices without a parent live in /sys/class/<classname>/ */
if (dev->class && (!parent || parent->class != dev->class))
return &dev->class->subsys.kobj;
+ /* all other devices keep their parent */
else if (parent)
return &parent->kobj;
return NULL;
}
+
+static inline void cleanup_device_parent(struct device *dev) {}
+static inline void cleanup_glue_dir(struct device *dev,
+ struct kobject *glue_dir) {}
#else
static struct kobject *virtual_device_parent(struct device *dev)
{
static struct kobject *virtual_dir = NULL;
if (!virtual_dir)
- virtual_dir = kobject_add_dir(&devices_subsys.kobj, "virtual");
+ virtual_dir = kobject_create_and_add("virtual",
+ &devices_kset->kobj);
return virtual_dir;
}
-static struct kobject * get_device_parent(struct device *dev,
- struct device *parent)
+static struct kobject *get_device_parent(struct device *dev,
+ struct device *parent)
{
+ int retval;
+
if (dev->class) {
struct kobject *kobj = NULL;
struct kobject *parent_kobj;
@@ -576,8 +574,8 @@ static struct kobject * get_device_parent(struct device *dev,
/*
* If we have no parent, we live in "virtual".
- * Class-devices with a bus-device as parent, live
- * in a class-directory to prevent namespace collisions.
+ * Class-devices with a non class-device as parent, live
+ * in a "glue" directory to prevent namespace collisions.
*/
if (parent == NULL)
parent_kobj = virtual_device_parent(dev);
@@ -598,25 +596,45 @@ static struct kobject * get_device_parent(struct device *dev,
return kobj;
/* or create a new class-directory at the parent device */
- return kobject_kset_add_dir(&dev->class->class_dirs,
- parent_kobj, dev->class->name);
+ k = kobject_create();
+ if (!k)
+ return NULL;
+ k->kset = &dev->class->class_dirs;
+ retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
+ if (retval < 0) {
+ kobject_put(k);
+ return NULL;
+ }
+ /* do not emit an uevent for this simple "glue" directory */
+ return k;
}
if (parent)
return &parent->kobj;
return NULL;
}
+
+static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
+{
+ /* see if we live in a "glue" directory */
+ if (!dev->class || glue_dir->kset != &dev->class->class_dirs)
+ return;
+
+ kobject_put(glue_dir);
+}
+
+static void cleanup_device_parent(struct device *dev)
+{
+ cleanup_glue_dir(dev, dev->kobj.parent);
+}
#endif
-static int setup_parent(struct device *dev, struct device *parent)
+static void setup_parent(struct device *dev, struct device *parent)
{
struct kobject *kobj;
kobj = get_device_parent(dev, parent);
- if (IS_ERR(kobj))
- return PTR_ERR(kobj);
if (kobj)
dev->kobj.parent = kobj;
- return 0;
}
static int device_add_class_symlinks(struct device *dev)
@@ -625,65 +643,76 @@ static int device_add_class_symlinks(struct device *dev)
if (!dev->class)
return 0;
+
error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
"subsystem");
if (error)
goto out;
- /*
- * If this is not a "fake" compatible device, then create the
- * symlink from the class to the device.
- */
- if (dev->kobj.parent != &dev->class->subsys.kobj) {
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+ /* stacked class devices need a symlink in the class directory */
+ if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ dev->type != &part_type) {
error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
dev->bus_id);
if (error)
goto out_subsys;
}
- if (dev->parent) {
-#ifdef CONFIG_SYSFS_DEPRECATED
- {
- struct device *parent = dev->parent;
- char *class_name;
-
- /*
- * In old sysfs stacked class devices had 'device'
- * link pointing to real device instead of parent
- */
- while (parent->class && !parent->bus && parent->parent)
- parent = parent->parent;
-
- error = sysfs_create_link(&dev->kobj,
- &parent->kobj,
- "device");
- if (error)
- goto out_busid;
- class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- error = sysfs_create_link(&dev->parent->kobj,
- &dev->kobj, class_name);
- kfree(class_name);
- if (error)
- goto out_device;
- }
-#else
- error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
+ if (dev->parent && dev->type != &part_type) {
+ struct device *parent = dev->parent;
+ char *class_name;
+
+ /*
+ * stacked class devices have the 'device' link
+ * pointing to the bus device instead of the parent
+ */
+ while (parent->class && !parent->bus && parent->parent)
+ parent = parent->parent;
+
+ error = sysfs_create_link(&dev->kobj,
+ &parent->kobj,
"device");
if (error)
goto out_busid;
-#endif
+
+ class_name = make_class_name(dev->class->name,
+ &dev->kobj);
+ if (class_name)
+ error = sysfs_create_link(&dev->parent->kobj,
+ &dev->kobj, class_name);
+ kfree(class_name);
+ if (error)
+ goto out_device;
}
return 0;
-#ifdef CONFIG_SYSFS_DEPRECATED
out_device:
- if (dev->parent)
+ if (dev->parent && dev->type != &part_type)
sysfs_remove_link(&dev->kobj, "device");
-#endif
out_busid:
- if (dev->kobj.parent != &dev->class->subsys.kobj)
+ if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ dev->type != &part_type)
sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+#else
+ /* link in the class directory pointing to the device */
+ error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
+ dev->bus_id);
+ if (error)
+ goto out_subsys;
+
+ if (dev->parent && dev->type != &part_type) {
+ error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
+ "device");
+ if (error)
+ goto out_busid;
+ }
+ return 0;
+
+out_busid:
+ sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+#endif
+
out_subsys:
sysfs_remove_link(&dev->kobj, "subsystem");
out:
@@ -694,8 +723,9 @@ static void device_remove_class_symlinks(struct device *dev)
{
if (!dev->class)
return;
- if (dev->parent) {
+
#ifdef CONFIG_SYSFS_DEPRECATED
+ if (dev->parent && dev->type != &part_type) {
char *class_name;
class_name = make_class_name(dev->class->name, &dev->kobj);
@@ -703,45 +733,59 @@ static void device_remove_class_symlinks(struct device *dev)
sysfs_remove_link(&dev->parent->kobj, class_name);
kfree(class_name);
}
-#endif
sysfs_remove_link(&dev->kobj, "device");
}
- if (dev->kobj.parent != &dev->class->subsys.kobj)
+
+ if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ dev->type != &part_type)
sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+#else
+ if (dev->parent && dev->type != &part_type)
+ sysfs_remove_link(&dev->kobj, "device");
+
+ sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+#endif
+
sysfs_remove_link(&dev->kobj, "subsystem");
}
/**
- * device_add - add device to device hierarchy.
- * @dev: device.
+ * device_add - add device to device hierarchy.
+ * @dev: device.
*
- * This is part 2 of device_register(), though may be called
- * separately _iff_ device_initialize() has been called separately.
+ * This is part 2 of device_register(), though may be called
+ * separately _iff_ device_initialize() has been called separately.
*
- * This adds it to the kobject hierarchy via kobject_add(), adds it
- * to the global and sibling lists for the device, then
- * adds it to the other relevant subsystems of the driver model.
+ * This adds it to the kobject hierarchy via kobject_add(), adds it
+ * to the global and sibling lists for the device, then
+ * adds it to the other relevant subsystems of the driver model.
*/
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
- int error = -EINVAL;
+ int error;
+
+ error = pm_sleep_lock();
+ if (error) {
+ dev_warn(dev, "Suspicious %s during suspend\n", __FUNCTION__);
+ dump_stack();
+ return error;
+ }
dev = get_device(dev);
- if (!dev || !strlen(dev->bus_id))
+ if (!dev || !strlen(dev->bus_id)) {
+ error = -EINVAL;
goto Error;
+ }
- pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
+ pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);
parent = get_device(dev->parent);
- error = setup_parent(dev, parent);
- if (error)
- goto Error;
+ setup_parent(dev, parent);
/* first, register with generic layer. */
- kobject_set_name(&dev->kobj, "%s", dev->bus_id);
- error = kobject_add(&dev->kobj);
+ error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
if (error)
goto Error;
@@ -751,7 +795,7 @@ int device_add(struct device *dev)
/* notify clients of device entry (new way) */
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
error = device_create_file(dev, &uevent_attr);
@@ -795,13 +839,14 @@ int device_add(struct device *dev)
}
Done:
put_device(dev);
+ pm_sleep_unlock();
return error;
BusError:
device_pm_remove(dev);
dpm_sysfs_remove(dev);
PMError:
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_attrs(dev);
AttrsError:
@@ -809,124 +854,84 @@ int device_add(struct device *dev)
SymlinkError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
-
- if (dev->class) {
- sysfs_remove_link(&dev->kobj, "subsystem");
- /* If this is not a "fake" compatible device, remove the
- * symlink from the class to the device. */
- if (dev->kobj.parent != &dev->class->subsys.kobj)
- sysfs_remove_link(&dev->class->subsys.kobj,
- dev->bus_id);
- if (parent) {
-#ifdef CONFIG_SYSFS_DEPRECATED
- char *class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- sysfs_remove_link(&dev->parent->kobj,
- class_name);
- kfree(class_name);
-#endif
- sysfs_remove_link(&dev->kobj, "device");
- }
- }
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
+ cleanup_device_parent(dev);
if (parent)
put_device(parent);
goto Done;
}
-
/**
- * device_register - register a device with the system.
- * @dev: pointer to the device structure
+ * device_register - register a device with the system.
+ * @dev: pointer to the device structure
*
- * This happens in two clean steps - initialize the device
- * and add it to the system. The two steps can be called
- * separately, but this is the easiest and most common.
- * I.e. you should only call the two helpers separately if
- * have a clearly defined need to use and refcount the device
- * before it is added to the hierarchy.
+ * This happens in two clean steps - initialize the device
+ * and add it to the system. The two steps can be called
+ * separately, but this is the easiest and most common.
+ * I.e. you should only call the two helpers separately if
+ * have a clearly defined need to use and refcount the device
+ * before it is added to the hierarchy.
*/
-
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
-
/**
- * get_device - increment reference count for device.
- * @dev: device.
+ * get_device - increment reference count for device.
+ * @dev: device.
*
- * This simply forwards the call to kobject_get(), though
- * we do take care to provide for the case that we get a NULL
- * pointer passed in.
+ * This simply forwards the call to kobject_get(), though
+ * we do take care to provide for the case that we get a NULL
+ * pointer passed in.
*/
-
-struct device * get_device(struct device * dev)
+struct device *get_device(struct device *dev)
{
return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
}
-
/**
- * put_device - decrement reference count.
- * @dev: device in question.
+ * put_device - decrement reference count.
+ * @dev: device in question.
*/
-void put_device(struct device * dev)
+void put_device(struct device *dev)
{
+ /* might_sleep(); */
if (dev)
kobject_put(&dev->kobj);
}
-
/**
- * device_del - delete device from system.
- * @dev: device.
+ * device_del - delete device from system.
+ * @dev: device.
*
- * This is the first part of the device unregistration
- * sequence. This removes the device from the lists we control
- * from here, has it removed from the other driver model
- * subsystems it was added to in device_add(), and removes it
- * from the kobject hierarchy.
+ * This is the first part of the device unregistration
+ * sequence. This removes the device from the lists we control
+ * from here, has it removed from the other driver model
+ * subsystems it was added to in device_add(), and removes it
+ * from the kobject hierarchy.
*
- * NOTE: this should be called manually _iff_ device_add() was
- * also called manually.
+ * NOTE: this should be called manually _iff_ device_add() was
+ * also called manually.
*/
-
-void device_del(struct device * dev)
+void device_del(struct device *dev)
{
- struct device * parent = dev->parent;
+ struct device *parent = dev->parent;
struct class_interface *class_intf;
+ device_pm_remove(dev);
if (parent)
klist_del(&dev->knode_parent);
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
if (dev->class) {
- sysfs_remove_link(&dev->kobj, "subsystem");
- /* If this is not a "fake" compatible device, remove the
- * symlink from the class to the device. */
- if (dev->kobj.parent != &dev->class->subsys.kobj)
- sysfs_remove_link(&dev->class->subsys.kobj,
- dev->bus_id);
- if (parent) {
-#ifdef CONFIG_SYSFS_DEPRECATED
- char *class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- sysfs_remove_link(&dev->parent->kobj,
- class_name);
- kfree(class_name);
-#endif
- sysfs_remove_link(&dev->kobj, "device");
- }
+ device_remove_class_symlinks(dev);
down(&dev->class->sem);
/* notify any interfaces that the device is now gone */
@@ -936,31 +941,6 @@ void device_del(struct device * dev)
/* remove the device from the class list */
list_del_init(&dev->node);
up(&dev->class->sem);
-
- /* If we live in a parent class-directory, unreference it */
- if (dev->kobj.parent->kset == &dev->class->class_dirs) {
- struct device *d;
- int other = 0;
-
- /*
- * if we are the last child of our class, delete
- * our class-directory at this parent
- */
- down(&dev->class->sem);
- list_for_each_entry(d, &dev->class->devices, node) {
- if (d == dev)
- continue;
- if (d->kobj.parent == dev->kobj.parent) {
- other = 1;
- break;
- }
- }
- if (!other)
- kobject_del(dev->kobj.parent);
-
- kobject_put(dev->kobj.parent);
- up(&dev->class->sem);
- }
}
device_remove_file(dev, &uevent_attr);
device_remove_attrs(dev);
@@ -979,57 +959,55 @@ void device_del(struct device * dev)
if (platform_notify_remove)
platform_notify_remove(dev);
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
- device_pm_remove(dev);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
+ cleanup_device_parent(dev);
kobject_del(&dev->kobj);
- if (parent)
- put_device(parent);
+ put_device(parent);
}
/**
- * device_unregister - unregister device from system.
- * @dev: device going away.
+ * device_unregister - unregister device from system.
+ * @dev: device going away.
*
- * We do this in two parts, like we do device_register(). First,
- * we remove it from all the subsystems with device_del(), then
- * we decrement the reference count via put_device(). If that
- * is the final reference count, the device will be cleaned up
- * via device_release() above. Otherwise, the structure will
- * stick around until the final reference to the device is dropped.
+ * We do this in two parts, like we do device_register(). First,
+ * we remove it from all the subsystems with device_del(), then
+ * we decrement the reference count via put_device(). If that
+ * is the final reference count, the device will be cleaned up
+ * via device_release() above. Otherwise, the structure will
+ * stick around until the final reference to the device is dropped.
*/
-void device_unregister(struct device * dev)
+void device_unregister(struct device *dev)
{
- pr_debug("DEV: Unregistering device. ID = '%s'\n", dev->bus_id);
+ pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);
device_del(dev);
put_device(dev);
}
-
-static struct device * next_device(struct klist_iter * i)
+static struct device *next_device(struct klist_iter *i)
{
- struct klist_node * n = klist_next(i);
+ struct klist_node *n = klist_next(i);
return n ? container_of(n, struct device, knode_parent) : NULL;
}
/**
- * device_for_each_child - device child iterator.
- * @parent: parent struct device.
- * @data: data for the callback.
- * @fn: function to be called for each device.
+ * device_for_each_child - device child iterator.
+ * @parent: parent struct device.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
*
- * Iterate over @parent's child devices, and call @fn for each,
- * passing it @data.
+ * Iterate over @parent's child devices, and call @fn for each,
+ * passing it @data.
*
- * We check the return of @fn each time. If it returns anything
- * other than 0, we break out and return that value.
+ * We check the return of @fn each time. If it returns anything
+ * other than 0, we break out and return that value.
*/
-int device_for_each_child(struct device * parent, void * data,
- int (*fn)(struct device *, void *))
+int device_for_each_child(struct device *parent, void *data,
+ int (*fn)(struct device *dev, void *data))
{
struct klist_iter i;
- struct device * child;
+ struct device *child;
int error = 0;
klist_iter_init(&parent->klist_children, &i);
@@ -1054,8 +1032,8 @@ int device_for_each_child(struct device * parent, void * data,
* current device can be obtained, this function will return to the caller
* and not iterate over any more devices.
*/
-struct device * device_find_child(struct device *parent, void *data,
- int (*match)(struct device *, void *))
+struct device *device_find_child(struct device *parent, void *data,
+ int (*match)(struct device *dev, void *data))
{
struct klist_iter i;
struct device *child;
@@ -1073,7 +1051,10 @@ struct device * device_find_child(struct device *parent, void *data,
int __init devices_init(void)
{
- return subsystem_register(&devices_subsys);
+ devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
+ if (!devices_kset)
+ return -ENOMEM;
+ return 0;
}
EXPORT_SYMBOL_GPL(device_for_each_child);
@@ -1094,7 +1075,7 @@ EXPORT_SYMBOL_GPL(device_remove_file);
static void device_create_release(struct device *dev)
{
- pr_debug("%s called for %s\n", __FUNCTION__, dev->bus_id);
+ pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);
kfree(dev);
}
@@ -1156,14 +1137,11 @@ error:
EXPORT_SYMBOL_GPL(device_create);
/**
- * device_destroy - removes a device that was created with device_create()
+ * find_device - finds a device that was created with device_create()
* @class: pointer to the struct class that this device was registered with
* @devt: the dev_t of the device that was previously registered
- *
- * This call unregisters and cleans up a device that was created with a
- * call to device_create().
*/
-void device_destroy(struct class *class, dev_t devt)
+static struct device *find_device(struct class *class, dev_t devt)
{
struct device *dev = NULL;
struct device *dev_tmp;
@@ -1176,12 +1154,54 @@ void device_destroy(struct class *class, dev_t devt)
}
}
up(&class->sem);
+ return dev;
+}
+
+/**
+ * device_destroy - removes a device that was created with device_create()
+ * @class: pointer to the struct class that this device was registered with
+ * @devt: the dev_t of the device that was previously registered
+ *
+ * This call unregisters and cleans up a device that was created with a
+ * call to device_create().
+ */
+void device_destroy(struct class *class, dev_t devt)
+{
+ struct device *dev;
+ dev = find_device(class, devt);
if (dev)
device_unregister(dev);
}
EXPORT_SYMBOL_GPL(device_destroy);
+#ifdef CONFIG_PM_SLEEP
+/**
+ * destroy_suspended_device - asks the PM core to remove a suspended device
+ * @class: pointer to the struct class that this device was registered with
+ * @devt: the dev_t of the device that was previously registered
+ *
+ * This call notifies the PM core of the necessity to unregister a suspended
+ * device created with a call to device_create() (devices cannot be
+ * unregistered directly while suspended, since the PM core holds their
+ * semaphores at that time).
+ *
+ * It can only be called within the scope of a system sleep transition. In
+ * practice this means it has to be directly or indirectly invoked either by
+ * a suspend or resume method, or by the PM core (e.g. via
+ * disable_nonboot_cpus() or enable_nonboot_cpus()).
+ */
+void destroy_suspended_device(struct class *class, dev_t devt)
+{
+ struct device *dev;
+
+ dev = find_device(class, devt);
+ if (dev)
+ device_pm_schedule_removal(dev);
+}
+EXPORT_SYMBOL_GPL(destroy_suspended_device);
+#endif /* CONFIG_PM_SLEEP */
+
/**
* device_rename - renames a device
* @dev: the pointer to the struct device to be renamed
@@ -1198,7 +1218,8 @@ int device_rename(struct device *dev, char *new_name)
if (!dev)
return -EINVAL;
- pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name);
+ pr_debug("device: '%s': %s: renaming to '%s'\n", dev->bus_id,
+ __FUNCTION__, new_name);
#ifdef CONFIG_SYSFS_DEPRECATED
if ((dev->class) && (dev->parent))
@@ -1279,8 +1300,7 @@ static int device_move_class_links(struct device *dev,
class_name);
if (error)
sysfs_remove_link(&dev->kobj, "device");
- }
- else
+ } else
error = 0;
out:
kfree(class_name);
@@ -1311,16 +1331,13 @@ int device_move(struct device *dev, struct device *new_parent)
return -EINVAL;
new_parent = get_device(new_parent);
- new_parent_kobj = get_device_parent (dev, new_parent);
- if (IS_ERR(new_parent_kobj)) {
- error = PTR_ERR(new_parent_kobj);
- put_device(new_parent);
- goto out;
- }
- pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id,
- new_parent ? new_parent->bus_id : "<NULL>");
+ new_parent_kobj = get_device_parent(dev, new_parent);
+
+ pr_debug("device: '%s': %s: moving to '%s'\n", dev->bus_id,
+ __FUNCTION__, new_parent ? new_parent->bus_id : "<NULL>");
error = kobject_move(&dev->kobj, new_parent_kobj);
if (error) {
+ cleanup_glue_dir(dev, new_parent_kobj);
put_device(new_parent);
goto out;
}
@@ -1343,6 +1360,7 @@ int device_move(struct device *dev, struct device *new_parent)
klist_add_tail(&dev->knode_parent,
&old_parent->klist_children);
}
+ cleanup_glue_dir(dev, new_parent_kobj);
put_device(new_parent);
goto out;
}
@@ -1352,5 +1370,23 @@ out:
put_device(dev);
return error;
}
-
EXPORT_SYMBOL_GPL(device_move);
+
+/**
+ * device_shutdown - call ->shutdown() on each device to shutdown.
+ */
+void device_shutdown(void)
+{
+ struct device *dev, *devn;
+
+ list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list,
+ kobj.entry) {
+ if (dev->bus && dev->bus->shutdown) {
+ dev_dbg(dev, "shutdown\n");
+ dev->bus->shutdown(dev);
+ } else if (dev->driver && dev->driver->shutdown) {
+ dev_dbg(dev, "shutdown\n");
+ dev->driver->shutdown(dev);
+ }
+ }
+}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 40545071e3c9..c5885f5ce0ac 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -14,7 +14,7 @@
#include "base.h"
struct sysdev_class cpu_sysdev_class = {
- set_kset_name("cpu"),
+ .name = "cpu",
};
EXPORT_SYMBOL(cpu_sysdev_class);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 7ac474db88c5..a5cde94bb982 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -1,18 +1,20 @@
/*
- * drivers/base/dd.c - The core device/driver interactions.
+ * drivers/base/dd.c - The core device/driver interactions.
*
- * This file contains the (sometimes tricky) code that controls the
- * interactions between devices and drivers, which primarily includes
- * driver binding and unbinding.
+ * This file contains the (sometimes tricky) code that controls the
+ * interactions between devices and drivers, which primarily includes
+ * driver binding and unbinding.
*
- * All of this code used to exist in drivers/base/bus.c, but was
- * relocated to here in the name of compartmentalization (since it wasn't
- * strictly code just for the 'struct bus_type'.
+ * All of this code used to exist in drivers/base/bus.c, but was
+ * relocated to here in the name of compartmentalization (since it wasn't
+ * strictly code just for the 'struct bus_type'.
*
- * Copyright (c) 2002-5 Patrick Mochel
- * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2002-5 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
*
- * This file is released under the GPLv2
+ * This file is released under the GPLv2
*/
#include <linux/device.h>
@@ -23,8 +25,6 @@
#include "base.h"
#include "power/power.h"
-#define to_drv(node) container_of(node, struct device_driver, kobj.entry)
-
static void driver_bound(struct device *dev)
{
@@ -34,27 +34,27 @@ static void driver_bound(struct device *dev)
return;
}
- pr_debug("bound device '%s' to driver '%s'\n",
- dev->bus_id, dev->driver->name);
+ pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->bus_id,
+ __FUNCTION__, dev->driver->name);
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
- klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
+ klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
}
static int driver_sysfs_add(struct device *dev)
{
int ret;
- ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
+ ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj));
if (ret == 0) {
- ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj,
+ ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
"driver");
if (ret)
- sysfs_remove_link(&dev->driver->kobj,
+ sysfs_remove_link(&dev->driver->p->kobj,
kobject_name(&dev->kobj));
}
return ret;
@@ -65,24 +65,24 @@ static void driver_sysfs_remove(struct device *dev)
struct device_driver *drv = dev->driver;
if (drv) {
- sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj));
+ sysfs_remove_link(&drv->p->kobj, kobject_name(&dev->kobj));
sysfs_remove_link(&dev->kobj, "driver");
}
}
/**
- * device_bind_driver - bind a driver to one device.
- * @dev: device.
+ * device_bind_driver - bind a driver to one device.
+ * @dev: device.
*
- * Allow manual attachment of a driver to a device.
- * Caller must have already set @dev->driver.
+ * Allow manual attachment of a driver to a device.
+ * Caller must have already set @dev->driver.
*
- * Note that this does not modify the bus reference count
- * nor take the bus's rwsem. Please verify those are accounted
- * for before calling this. (It is ok to call with no other effort
- * from a driver's probe() method.)
+ * Note that this does not modify the bus reference count
+ * nor take the bus's rwsem. Please verify those are accounted
+ * for before calling this. (It is ok to call with no other effort
+ * from a driver's probe() method.)
*
- * This function must be called with @dev->sem held.
+ * This function must be called with @dev->sem held.
*/
int device_bind_driver(struct device *dev)
{
@@ -93,6 +93,7 @@ int device_bind_driver(struct device *dev)
driver_bound(dev);
return ret;
}
+EXPORT_SYMBOL_GPL(device_bind_driver);
static atomic_t probe_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
@@ -102,8 +103,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
int ret = 0;
atomic_inc(&probe_count);
- pr_debug("%s: Probing driver %s with device %s\n",
- drv->bus->name, drv->name, dev->bus_id);
+ pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
+ drv->bus->name, __FUNCTION__, drv->name, dev->bus_id);
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
@@ -125,8 +126,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
driver_bound(dev);
ret = 1;
- pr_debug("%s: Bound Device %s to Driver %s\n",
- drv->bus->name, dev->bus_id, drv->name);
+ pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
+ drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
goto done;
probe_failed:
@@ -183,7 +184,7 @@ int driver_probe_done(void)
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
-int driver_probe_device(struct device_driver * drv, struct device * dev)
+int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
@@ -192,8 +193,8 @@ int driver_probe_device(struct device_driver * drv, struct device * dev)
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
- pr_debug("%s: Matched Device %s with Driver %s\n",
- drv->bus->name, dev->bus_id, drv->name);
+ pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
+ drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
ret = really_probe(dev, drv);
@@ -201,27 +202,27 @@ done:
return ret;
}
-static int __device_attach(struct device_driver * drv, void * data)
+static int __device_attach(struct device_driver *drv, void *data)
{
- struct device * dev = data;
+ struct device *dev = data;
return driver_probe_device(drv, dev);
}
/**
- * device_attach - try to attach device to a driver.
- * @dev: device.
+ * device_attach - try to attach device to a driver.
+ * @dev: device.
*
- * Walk the list of drivers that the bus has and call
- * driver_probe_device() for each pair. If a compatible
- * pair is found, break out and return.
+ * Walk the list of drivers that the bus has and call
+ * driver_probe_device() for each pair. If a compatible
+ * pair is found, break out and return.
*
- * Returns 1 if the device was bound to a driver;
- * 0 if no matching device was found;
- * -ENODEV if the device is not registered.
+ * Returns 1 if the device was bound to a driver;
+ * 0 if no matching device was found;
+ * -ENODEV if the device is not registered.
*
- * When called for a USB interface, @dev->parent->sem must be held.
+ * When called for a USB interface, @dev->parent->sem must be held.
*/
-int device_attach(struct device * dev)
+int device_attach(struct device *dev)
{
int ret = 0;
@@ -240,10 +241,11 @@ int device_attach(struct device * dev)
up(&dev->sem);
return ret;
}
+EXPORT_SYMBOL_GPL(device_attach);
-static int __driver_attach(struct device * dev, void * data)
+static int __driver_attach(struct device *dev, void *data)
{
- struct device_driver * drv = data;
+ struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
@@ -268,35 +270,35 @@ static int __driver_attach(struct device * dev, void * data)
}
/**
- * driver_attach - try to bind driver to devices.
- * @drv: driver.
+ * driver_attach - try to bind driver to devices.
+ * @drv: driver.
*
- * Walk the list of devices that the bus has on it and try to
- * match the driver with each one. If driver_probe_device()
- * returns 0 and the @dev->driver is set, we've found a
- * compatible pair.
+ * Walk the list of devices that the bus has on it and try to
+ * match the driver with each one. If driver_probe_device()
+ * returns 0 and the @dev->driver is set, we've found a
+ * compatible pair.
*/
-int driver_attach(struct device_driver * drv)
+int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
+EXPORT_SYMBOL_GPL(driver_attach);
/*
- * __device_release_driver() must be called with @dev->sem held.
- * When called for a USB interface, @dev->parent->sem must be held as well.
+ * __device_release_driver() must be called with @dev->sem held.
+ * When called for a USB interface, @dev->parent->sem must be held as well.
*/
-static void __device_release_driver(struct device * dev)
+static void __device_release_driver(struct device *dev)
{
- struct device_driver * drv;
+ struct device_driver *drv;
- drv = get_driver(dev->driver);
+ drv = dev->driver;
if (drv) {
driver_sysfs_remove(dev);
sysfs_remove_link(&dev->kobj, "driver");
- klist_remove(&dev->knode_driver);
if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_UNBIND_DRIVER,
dev);
@@ -306,18 +308,18 @@ static void __device_release_driver(struct device * dev)
drv->remove(dev);
devres_release_all(dev);
dev->driver = NULL;
- put_driver(drv);
+ klist_remove(&dev->knode_driver);
}
}
/**
- * device_release_driver - manually detach device from driver.
- * @dev: device.
+ * device_release_driver - manually detach device from driver.
+ * @dev: device.
*
- * Manually detach device from driver.
- * When called for a USB interface, @dev->parent->sem must be held.
+ * Manually detach device from driver.
+ * When called for a USB interface, @dev->parent->sem must be held.
*/
-void device_release_driver(struct device * dev)
+void device_release_driver(struct device *dev)
{
/*
* If anyone calls device_release_driver() recursively from
@@ -328,26 +330,26 @@ void device_release_driver(struct device * dev)
__device_release_driver(dev);
up(&dev->sem);
}
-
+EXPORT_SYMBOL_GPL(device_release_driver);
/**
* driver_detach - detach driver from all devices it controls.
* @drv: driver.
*/
-void driver_detach(struct device_driver * drv)
+void driver_detach(struct device_driver *drv)
{
- struct device * dev;
+ struct device *dev;
for (;;) {
- spin_lock(&drv->klist_devices.k_lock);
- if (list_empty(&drv->klist_devices.k_list)) {
- spin_unlock(&drv->klist_devices.k_lock);
+ spin_lock(&drv->p->klist_devices.k_lock);
+ if (list_empty(&drv->p->klist_devices.k_list)) {
+ spin_unlock(&drv->p->klist_devices.k_lock);
break;
}
- dev = list_entry(drv->klist_devices.k_list.prev,
+ dev = list_entry(drv->p->klist_devices.k_list.prev,
struct device, knode_driver.n_node);
get_device(dev);
- spin_unlock(&drv->klist_devices.k_lock);
+ spin_unlock(&drv->p->klist_devices.k_lock);
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
@@ -360,9 +362,3 @@ void driver_detach(struct device_driver * drv)
put_device(dev);
}
}
-
-EXPORT_SYMBOL_GPL(device_bind_driver);
-EXPORT_SYMBOL_GPL(device_release_driver);
-EXPORT_SYMBOL_GPL(device_attach);
-EXPORT_SYMBOL_GPL(driver_attach);
-
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index eb11475293ed..a35f04121a00 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -3,6 +3,8 @@
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
*
* This file is released under the GPLv2
*
@@ -15,46 +17,42 @@
#include "base.h"
#define to_dev(node) container_of(node, struct device, driver_list)
-#define to_drv(obj) container_of(obj, struct device_driver, kobj)
-static struct device * next_device(struct klist_iter * i)
+static struct device *next_device(struct klist_iter *i)
{
- struct klist_node * n = klist_next(i);
+ struct klist_node *n = klist_next(i);
return n ? container_of(n, struct device, knode_driver) : NULL;
}
/**
- * driver_for_each_device - Iterator for devices bound to a driver.
- * @drv: Driver we're iterating.
- * @start: Device to begin with
- * @data: Data to pass to the callback.
- * @fn: Function to call for each device.
+ * driver_for_each_device - Iterator for devices bound to a driver.
+ * @drv: Driver we're iterating.
+ * @start: Device to begin with
+ * @data: Data to pass to the callback.
+ * @fn: Function to call for each device.
*
- * Iterate over the @drv's list of devices calling @fn for each one.
+ * Iterate over the @drv's list of devices calling @fn for each one.
*/
-
-int driver_for_each_device(struct device_driver * drv, struct device * start,
- void * data, int (*fn)(struct device *, void *))
+int driver_for_each_device(struct device_driver *drv, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
- struct device * dev;
+ struct device *dev;
int error = 0;
if (!drv)
return -EINVAL;
- klist_iter_init_node(&drv->klist_devices, &i,
+ klist_iter_init_node(&drv->p->klist_devices, &i,
start ? &start->knode_driver : NULL);
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
-
EXPORT_SYMBOL_GPL(driver_for_each_device);
-
/**
* driver_find_device - device iterator for locating a particular device.
* @drv: The device's driver
@@ -70,9 +68,9 @@ EXPORT_SYMBOL_GPL(driver_for_each_device);
* if it does. If the callback returns non-zero, this function will
* return to the caller and not iterate over any more devices.
*/
-struct device * driver_find_device(struct device_driver *drv,
- struct device * start, void * data,
- int (*match)(struct device *, void *))
+struct device *driver_find_device(struct device_driver *drv,
+ struct device *start, void *data,
+ int (*match)(struct device *dev, void *data))
{
struct klist_iter i;
struct device *dev;
@@ -80,7 +78,7 @@ struct device * driver_find_device(struct device_driver *drv,
if (!drv)
return NULL;
- klist_iter_init_node(&drv->klist_devices, &i,
+ klist_iter_init_node(&drv->p->klist_devices, &i,
(start ? &start->knode_driver : NULL));
while ((dev = next_device(&i)))
if (match(dev, data) && get_device(dev))
@@ -91,111 +89,179 @@ struct device * driver_find_device(struct device_driver *drv,
EXPORT_SYMBOL_GPL(driver_find_device);
/**
- * driver_create_file - create sysfs file for driver.
- * @drv: driver.
- * @attr: driver attribute descriptor.
+ * driver_create_file - create sysfs file for driver.
+ * @drv: driver.
+ * @attr: driver attribute descriptor.
*/
-
-int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
+int driver_create_file(struct device_driver *drv,
+ struct driver_attribute *attr)
{
int error;
if (get_driver(drv)) {
- error = sysfs_create_file(&drv->kobj, &attr->attr);
+ error = sysfs_create_file(&drv->p->kobj, &attr->attr);
put_driver(drv);
} else
error = -EINVAL;
return error;
}
-
+EXPORT_SYMBOL_GPL(driver_create_file);
/**
- * driver_remove_file - remove sysfs file for driver.
- * @drv: driver.
- * @attr: driver attribute descriptor.
+ * driver_remove_file - remove sysfs file for driver.
+ * @drv: driver.
+ * @attr: driver attribute descriptor.
*/
-
-void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr)
+void driver_remove_file(struct device_driver *drv,
+ struct driver_attribute *attr)
{
if (get_driver(drv)) {
- sysfs_remove_file(&drv->kobj, &attr->attr);
+ sysfs_remove_file(&drv->p->kobj, &attr->attr);
put_driver(drv);
}
}
-
+EXPORT_SYMBOL_GPL(driver_remove_file);
/**
- * get_driver - increment driver reference count.
- * @drv: driver.
+ * driver_add_kobj - add a kobject below the specified driver
+ *
+ * You really don't want to do this, this is only here due to one looney
+ * iseries driver, go poke those developers if you are annoyed about
+ * this...
*/
-struct device_driver * get_driver(struct device_driver * drv)
+int driver_add_kobj(struct device_driver *drv, struct kobject *kobj,
+ const char *fmt, ...)
{
- return drv ? to_drv(kobject_get(&drv->kobj)) : NULL;
+ va_list args;
+ char *name;
+
+ va_start(args, fmt);
+ name = kvasprintf(GFP_KERNEL, fmt, args);
+ va_end(args);
+
+ if (!name)
+ return -ENOMEM;
+
+ return kobject_add(kobj, &drv->p->kobj, "%s", name);
}
+EXPORT_SYMBOL_GPL(driver_add_kobj);
+
+/**
+ * get_driver - increment driver reference count.
+ * @drv: driver.
+ */
+struct device_driver *get_driver(struct device_driver *drv)
+{
+ if (drv) {
+ struct driver_private *priv;
+ struct kobject *kobj;
+ kobj = kobject_get(&drv->p->kobj);
+ priv = to_driver(kobj);
+ return priv->driver;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(get_driver);
/**
- * put_driver - decrement driver's refcount.
- * @drv: driver.
+ * put_driver - decrement driver's refcount.
+ * @drv: driver.
*/
-void put_driver(struct device_driver * drv)
+void put_driver(struct device_driver *drv)
+{
+ kobject_put(&drv->p->kobj);
+}
+EXPORT_SYMBOL_GPL(put_driver);
+
+static int driver_add_groups(struct device_driver *drv,
+ struct attribute_group **groups)
{
- kobject_put(&drv->kobj);
+ int error = 0;
+ int i;
+
+ if (groups) {
+ for (i = 0; groups[i]; i++) {
+ error = sysfs_create_group(&drv->p->kobj, groups[i]);
+ if (error) {
+ while (--i >= 0)
+ sysfs_remove_group(&drv->p->kobj,
+ groups[i]);
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+static void driver_remove_groups(struct device_driver *drv,
+ struct attribute_group **groups)
+{
+ int i;
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ sysfs_remove_group(&drv->p->kobj, groups[i]);
}
/**
- * driver_register - register driver with bus
- * @drv: driver to register
+ * driver_register - register driver with bus
+ * @drv: driver to register
*
- * We pass off most of the work to the bus_add_driver() call,
- * since most of the things we have to do deal with the bus
- * structures.
+ * We pass off most of the work to the bus_add_driver() call,
+ * since most of the things we have to do deal with the bus
+ * structures.
*/
-int driver_register(struct device_driver * drv)
+int driver_register(struct device_driver *drv)
{
+ int ret;
+
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
- (drv->bus->shutdown && drv->shutdown)) {
- printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
- }
- klist_init(&drv->klist_devices, NULL, NULL);
- return bus_add_driver(drv);
+ (drv->bus->shutdown && drv->shutdown))
+ printk(KERN_WARNING "Driver '%s' needs updating - please use "
+ "bus_type methods\n", drv->name);
+ ret = bus_add_driver(drv);
+ if (ret)
+ return ret;
+ ret = driver_add_groups(drv, drv->groups);
+ if (ret)
+ bus_remove_driver(drv);
+ return ret;
}
+EXPORT_SYMBOL_GPL(driver_register);
/**
- * driver_unregister - remove driver from system.
- * @drv: driver.
+ * driver_unregister - remove driver from system.
+ * @drv: driver.
*
- * Again, we pass off most of the work to the bus-level call.
+ * Again, we pass off most of the work to the bus-level call.
*/
-
-void driver_unregister(struct device_driver * drv)
+void driver_unregister(struct device_driver *drv)
{
+ driver_remove_groups(drv, drv->groups);
bus_remove_driver(drv);
}
+EXPORT_SYMBOL_GPL(driver_unregister);
/**
- * driver_find - locate driver on a bus by its name.
- * @name: name of the driver.
- * @bus: bus to scan for the driver.
+ * driver_find - locate driver on a bus by its name.
+ * @name: name of the driver.
+ * @bus: bus to scan for the driver.
*
- * Call kset_find_obj() to iterate over list of drivers on
- * a bus to find driver by name. Return driver if found.
+ * Call kset_find_obj() to iterate over list of drivers on
+ * a bus to find driver by name. Return driver if found.
*
- * Note that kset_find_obj increments driver's reference count.
+ * Note that kset_find_obj increments driver's reference count.
*/
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
- struct kobject *k = kset_find_obj(&bus->drivers, name);
- if (k)
- return to_drv(k);
+ struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
+ struct driver_private *priv;
+
+ if (k) {
+ priv = to_driver(k);
+ return priv->driver;
+ }
return NULL;
}
-
-EXPORT_SYMBOL_GPL(driver_register);
-EXPORT_SYMBOL_GPL(driver_unregister);
-EXPORT_SYMBOL_GPL(get_driver);
-EXPORT_SYMBOL_GPL(put_driver);
EXPORT_SYMBOL_GPL(driver_find);
-
-EXPORT_SYMBOL_GPL(driver_create_file);
-EXPORT_SYMBOL_GPL(driver_remove_file);
diff --git a/drivers/base/firmware.c b/drivers/base/firmware.c
index 90c862932169..113815556809 100644
--- a/drivers/base/firmware.c
+++ b/drivers/base/firmware.c
@@ -3,11 +3,11 @@
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
*
* This file is released under the GPLv2
- *
*/
-
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -15,23 +15,13 @@
#include "base.h"
-static decl_subsys(firmware, NULL, NULL);
-
-int firmware_register(struct kset *s)
-{
- kobj_set_kset_s(s, firmware_subsys);
- return subsystem_register(s);
-}
-
-void firmware_unregister(struct kset *s)
-{
- subsystem_unregister(s);
-}
+struct kobject *firmware_kobj;
+EXPORT_SYMBOL_GPL(firmware_kobj);
int __init firmware_init(void)
{
- return subsystem_register(&firmware_subsys);
+ firmware_kobj = kobject_create_and_add("firmware", NULL);
+ if (!firmware_kobj)
+ return -ENOMEM;
+ return 0;
}
-
-EXPORT_SYMBOL_GPL(firmware_register);
-EXPORT_SYMBOL_GPL(firmware_unregister);
diff --git a/drivers/base/hypervisor.c b/drivers/base/hypervisor.c
index 7080b413ddc9..6428cba3aadd 100644
--- a/drivers/base/hypervisor.c
+++ b/drivers/base/hypervisor.c
@@ -2,19 +2,23 @@
* hypervisor.c - /sys/hypervisor subsystem.
*
* Copyright (C) IBM Corp. 2006
+ * Copyright (C) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2007 Novell Inc.
*
* This file is released under the GPLv2
*/
#include <linux/kobject.h>
#include <linux/device.h>
-
#include "base.h"
-decl_subsys(hypervisor, NULL, NULL);
-EXPORT_SYMBOL_GPL(hypervisor_subsys);
+struct kobject *hypervisor_kobj;
+EXPORT_SYMBOL_GPL(hypervisor_kobj);
int __init hypervisor_init(void)
{
- return subsystem_register(&hypervisor_subsys);
+ hypervisor_kobj = kobject_create_and_add("hypervisor", NULL);
+ if (!hypervisor_kobj)
+ return -ENOMEM;
+ return 0;
}
diff --git a/drivers/base/init.c b/drivers/base/init.c
index 37138154f9e8..7bd9b6a5b01f 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -1,10 +1,8 @@
/*
- *
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
*
* This file is released under the GPLv2
- *
*/
#include <linux/device.h>
@@ -14,12 +12,11 @@
#include "base.h"
/**
- * driver_init - initialize driver model.
+ * driver_init - initialize driver model.
*
- * Call the driver model init functions to initialize their
- * subsystems. Called early from init/main.c.
+ * Call the driver model init functions to initialize their
+ * subsystems. Called early from init/main.c.
*/
-
void __init driver_init(void)
{
/* These are the core pieces */
@@ -36,5 +33,4 @@ void __init driver_init(void)
system_bus_init();
cpu_dev_init();
memory_dev_init();
- attribute_container_init();
}
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 7868707c7eda..7ae413fdd5fc 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -26,7 +26,7 @@
#define MEMORY_CLASS_NAME "memory"
static struct sysdev_class memory_sysdev_class = {
- set_kset_name(MEMORY_CLASS_NAME),
+ .name = MEMORY_CLASS_NAME,
};
static const char *memory_uevent_name(struct kset *kset, struct kobject *kobj)
diff --git a/drivers/base/module.c b/drivers/base/module.c
new file mode 100644
index 000000000000..103be9cacb05
--- /dev/null
+++ b/drivers/base/module.c
@@ -0,0 +1,94 @@
+/*
+ * module.c - module sysfs fun for drivers
+ *
+ * This file is released under the GPLv2
+ *
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "base.h"
+
+static char *make_driver_name(struct device_driver *drv)
+{
+ char *driver_name;
+
+ driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2,
+ GFP_KERNEL);
+ if (!driver_name)
+ return NULL;
+
+ sprintf(driver_name, "%s:%s", drv->bus->name, drv->name);
+ return driver_name;
+}
+
+static void module_create_drivers_dir(struct module_kobject *mk)
+{
+ if (!mk || mk->drivers_dir)
+ return;
+
+ mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj);
+}
+
+void module_add_driver(struct module *mod, struct device_driver *drv)
+{
+ char *driver_name;
+ int no_warn;
+ struct module_kobject *mk = NULL;
+
+ if (!drv)
+ return;
+
+ if (mod)
+ mk = &mod->mkobj;
+ else if (drv->mod_name) {
+ struct kobject *mkobj;
+
+ /* Lookup built-in module entry in /sys/modules */
+ mkobj = kset_find_obj(module_kset, drv->mod_name);
+ if (mkobj) {
+ mk = container_of(mkobj, struct module_kobject, kobj);
+ /* remember our module structure */
+ drv->p->mkobj = mk;
+ /* kset_find_obj took a reference */
+ kobject_put(mkobj);
+ }
+ }
+
+ if (!mk)
+ return;
+
+ /* Don't check return codes; these calls are idempotent */
+ no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
+ driver_name = make_driver_name(drv);
+ if (driver_name) {
+ module_create_drivers_dir(mk);
+ no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
+ driver_name);
+ kfree(driver_name);
+ }
+}
+
+void module_remove_driver(struct device_driver *drv)
+{
+ struct module_kobject *mk = NULL;
+ char *driver_name;
+
+ if (!drv)
+ return;
+
+ sysfs_remove_link(&drv->p->kobj, "module");
+
+ if (drv->owner)
+ mk = &drv->owner->mkobj;
+ else if (drv->p->mkobj)
+ mk = drv->p->mkobj;
+ if (mk && mk->drivers_dir) {
+ driver_name = make_driver_name(drv);
+ if (driver_name) {
+ sysfs_remove_link(mk->drivers_dir, driver_name);
+ kfree(driver_name);
+ }
+ }
+}
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 88eeed72b5d6..e59861f18ce5 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -15,7 +15,7 @@
#include <linux/device.h>
static struct sysdev_class node_class = {
- set_kset_name("node"),
+ .name = "node",
};
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index fb5609241482..efaf282c438c 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -20,7 +20,8 @@
#include "base.h"
-#define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver))
+#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
+ driver))
struct device platform_bus = {
.bus_id = "platform",
@@ -28,14 +29,13 @@ struct device platform_bus = {
EXPORT_SYMBOL_GPL(platform_bus);
/**
- * platform_get_resource - get a resource for a device
- * @dev: platform device
- * @type: resource type
- * @num: resource index
+ * platform_get_resource - get a resource for a device
+ * @dev: platform device
+ * @type: resource type
+ * @num: resource index
*/
-struct resource *
-platform_get_resource(struct platform_device *dev, unsigned int type,
- unsigned int num)
+struct resource *platform_get_resource(struct platform_device *dev,
+ unsigned int type, unsigned int num)
{
int i;
@@ -43,8 +43,7 @@ platform_get_resource(struct platform_device *dev, unsigned int type,
struct resource *r = &dev->resource[i];
if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
- IORESOURCE_IRQ|IORESOURCE_DMA))
- == type)
+ IORESOURCE_IRQ|IORESOURCE_DMA)) == type)
if (num-- == 0)
return r;
}
@@ -53,9 +52,9 @@ platform_get_resource(struct platform_device *dev, unsigned int type,
EXPORT_SYMBOL_GPL(platform_get_resource);
/**
- * platform_get_irq - get an IRQ for a device
- * @dev: platform device
- * @num: IRQ number index
+ * platform_get_irq - get an IRQ for a device
+ * @dev: platform device
+ * @num: IRQ number index
*/
int platform_get_irq(struct platform_device *dev, unsigned int num)
{
@@ -66,14 +65,13 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
EXPORT_SYMBOL_GPL(platform_get_irq);
/**
- * platform_get_resource_byname - get a resource for a device by name
- * @dev: platform device
- * @type: resource type
- * @name: resource name
+ * platform_get_resource_byname - get a resource for a device by name
+ * @dev: platform device
+ * @type: resource type
+ * @name: resource name
*/
-struct resource *
-platform_get_resource_byname(struct platform_device *dev, unsigned int type,
- char *name)
+struct resource *platform_get_resource_byname(struct platform_device *dev,
+ unsigned int type, char *name)
{
int i;
@@ -90,22 +88,23 @@ platform_get_resource_byname(struct platform_device *dev, unsigned int type,
EXPORT_SYMBOL_GPL(platform_get_resource_byname);
/**
- * platform_get_irq - get an IRQ for a device
- * @dev: platform device
- * @name: IRQ name
+ * platform_get_irq - get an IRQ for a device
+ * @dev: platform device
+ * @name: IRQ name
*/
int platform_get_irq_byname(struct platform_device *dev, char *name)
{
- struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);
+ struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ,
+ name);
return r ? r->start : -ENXIO;
}
EXPORT_SYMBOL_GPL(platform_get_irq_byname);
/**
- * platform_add_devices - add a numbers of platform devices
- * @devs: array of platform devices to add
- * @num: number of platform devices in array
+ * platform_add_devices - add a numbers of platform devices
+ * @devs: array of platform devices to add
+ * @num: number of platform devices in array
*/
int platform_add_devices(struct platform_device **devs, int num)
{
@@ -130,12 +129,11 @@ struct platform_object {
};
/**
- * platform_device_put
- * @pdev: platform device to free
+ * platform_device_put
+ * @pdev: platform device to free
*
- * Free all memory associated with a platform device. This function
- * must _only_ be externally called in error cases. All other usage
- * is a bug.
+ * Free all memory associated with a platform device. This function must
+ * _only_ be externally called in error cases. All other usage is a bug.
*/
void platform_device_put(struct platform_device *pdev)
{
@@ -146,7 +144,8 @@ EXPORT_SYMBOL_GPL(platform_device_put);
static void platform_device_release(struct device *dev)
{
- struct platform_object *pa = container_of(dev, struct platform_object, pdev.dev);
+ struct platform_object *pa = container_of(dev, struct platform_object,
+ pdev.dev);
kfree(pa->pdev.dev.platform_data);
kfree(pa->pdev.resource);
@@ -154,12 +153,12 @@ static void platform_device_release(struct device *dev)
}
/**
- * platform_device_alloc
- * @name: base name of the device we're adding
- * @id: instance id
+ * platform_device_alloc
+ * @name: base name of the device we're adding
+ * @id: instance id
*
- * Create a platform device object which can have other objects attached
- * to it, and which will have attached objects freed when it is released.
+ * Create a platform device object which can have other objects attached
+ * to it, and which will have attached objects freed when it is released.
*/
struct platform_device *platform_device_alloc(const char *name, int id)
{
@@ -179,16 +178,17 @@ struct platform_device *platform_device_alloc(const char *name, int id)
EXPORT_SYMBOL_GPL(platform_device_alloc);
/**
- * platform_device_add_resources
- * @pdev: platform device allocated by platform_device_alloc to add resources to
- * @res: set of resources that needs to be allocated for the device
- * @num: number of resources
+ * platform_device_add_resources
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @res: set of resources that needs to be allocated for the device
+ * @num: number of resources
*
- * Add a copy of the resources to the platform device. The memory
- * associated with the resources will be freed when the platform
- * device is released.
+ * Add a copy of the resources to the platform device. The memory
+ * associated with the resources will be freed when the platform device is
+ * released.
*/
-int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num)
+int platform_device_add_resources(struct platform_device *pdev,
+ struct resource *res, unsigned int num)
{
struct resource *r;
@@ -203,16 +203,17 @@ int platform_device_add_resources(struct platform_device *pdev, struct resource
EXPORT_SYMBOL_GPL(platform_device_add_resources);
/**
- * platform_device_add_data
- * @pdev: platform device allocated by platform_device_alloc to add resources to
- * @data: platform specific data for this platform device
- * @size: size of platform specific data
+ * platform_device_add_data
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @data: platform specific data for this platform device
+ * @size: size of platform specific data
*
- * Add a copy of platform specific data to the platform device's platform_data
- * pointer. The memory associated with the platform data will be freed
- * when the platform device is released.
+ * Add a copy of platform specific data to the platform device's
+ * platform_data pointer. The memory associated with the platform data
+ * will be freed when the platform device is released.
*/
-int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size)
+int platform_device_add_data(struct platform_device *pdev, const void *data,
+ size_t size)
{
void *d;
@@ -226,11 +227,11 @@ int platform_device_add_data(struct platform_device *pdev, const void *data, siz
EXPORT_SYMBOL_GPL(platform_device_add_data);
/**
- * platform_device_add - add a platform device to device hierarchy
- * @pdev: platform device we're adding
+ * platform_device_add - add a platform device to device hierarchy
+ * @pdev: platform device we're adding
*
- * This is part 2 of platform_device_register(), though may be called
- * separately _iff_ pdev was allocated by platform_device_alloc().
+ * This is part 2 of platform_device_register(), though may be called
+ * separately _iff_ pdev was allocated by platform_device_alloc().
*/
int platform_device_add(struct platform_device *pdev)
{
@@ -289,13 +290,12 @@ int platform_device_add(struct platform_device *pdev)
EXPORT_SYMBOL_GPL(platform_device_add);
/**
- * platform_device_del - remove a platform-level device
- * @pdev: platform device we're removing
+ * platform_device_del - remove a platform-level device
+ * @pdev: platform device we're removing
*
- * Note that this function will also release all memory- and port-based
- * resources owned by the device (@dev->resource). This function
- * must _only_ be externally called in error cases. All other usage
- * is a bug.
+ * Note that this function will also release all memory- and port-based
+ * resources owned by the device (@dev->resource). This function must
+ * _only_ be externally called in error cases. All other usage is a bug.
*/
void platform_device_del(struct platform_device *pdev)
{
@@ -314,11 +314,10 @@ void platform_device_del(struct platform_device *pdev)
EXPORT_SYMBOL_GPL(platform_device_del);
/**
- * platform_device_register - add a platform-level device
- * @pdev: platform device we're adding
- *
+ * platform_device_register - add a platform-level device
+ * @pdev: platform device we're adding
*/
-int platform_device_register(struct platform_device * pdev)
+int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
@@ -326,14 +325,14 @@ int platform_device_register(struct platform_device * pdev)
EXPORT_SYMBOL_GPL(platform_device_register);
/**
- * platform_device_unregister - unregister a platform-level device
- * @pdev: platform device we're unregistering
+ * platform_device_unregister - unregister a platform-level device
+ * @pdev: platform device we're unregistering
*
- * Unregistration is done in 2 steps. First we release all resources
- * and remove it from the subsystem, then we drop reference count by
- * calling platform_device_put().
+ * Unregistration is done in 2 steps. First we release all resources
+ * and remove it from the subsystem, then we drop reference count by
+ * calling platform_device_put().
*/
-void platform_device_unregister(struct platform_device * pdev)
+void platform_device_unregister(struct platform_device *pdev)
{
platform_device_del(pdev);
platform_device_put(pdev);
@@ -341,27 +340,29 @@ void platform_device_unregister(struct platform_device * pdev)
EXPORT_SYMBOL_GPL(platform_device_unregister);
/**
- * platform_device_register_simple
- * @name: base name of the device we're adding
- * @id: instance id
- * @res: set of resources that needs to be allocated for the device
- * @num: number of resources
+ * platform_device_register_simple
+ * @name: base name of the device we're adding
+ * @id: instance id
+ * @res: set of resources that needs to be allocated for the device
+ * @num: number of resources
*
- * This function creates a simple platform device that requires minimal
- * resource and memory management. Canned release function freeing
- * memory allocated for the device allows drivers using such devices
- * to be unloaded without waiting for the last reference to the device
- * to be dropped.
+ * This function creates a simple platform device that requires minimal
+ * resource and memory management. Canned release function freeing memory
+ * allocated for the device allows drivers using such devices to be
+ * unloaded without waiting for the last reference to the device to be
+ * dropped.
*
- * This interface is primarily intended for use with legacy drivers
- * which probe hardware directly. Because such drivers create sysfs
- * device nodes themselves, rather than letting system infrastructure
- * handle such device enumeration tasks, they don't fully conform to
- * the Linux driver model. In particular, when such drivers are built
- * as modules, they can't be "hotplugged".
+ * This interface is primarily intended for use with legacy drivers which
+ * probe hardware directly. Because such drivers create sysfs device nodes
+ * themselves, rather than letting system infrastructure handle such device
+ * enumeration tasks, they don't fully conform to the Linux driver model.
+ * In particular, when such drivers are built as modules, they can't be
+ * "hotplugged".
*/
-struct platform_device *platform_device_register_simple(char *name, int id,
- struct resource *res, unsigned int num)
+struct platform_device *platform_device_register_simple(const char *name,
+ int id,
+ struct resource *res,
+ unsigned int num)
{
struct platform_device *pdev;
int retval;
@@ -436,8 +437,8 @@ static int platform_drv_resume(struct device *_dev)
}
/**
- * platform_driver_register
- * @drv: platform driver structure
+ * platform_driver_register
+ * @drv: platform driver structure
*/
int platform_driver_register(struct platform_driver *drv)
{
@@ -457,8 +458,8 @@ int platform_driver_register(struct platform_driver *drv)
EXPORT_SYMBOL_GPL(platform_driver_register);
/**
- * platform_driver_unregister
- * @drv: platform driver structure
+ * platform_driver_unregister
+ * @drv: platform driver structure
*/
void platform_driver_unregister(struct platform_driver *drv)
{
@@ -497,12 +498,12 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv,
* if the probe was successful, and make sure any forced probes of
* new devices fail.
*/
- spin_lock(&platform_bus_type.klist_drivers.k_lock);
+ spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
drv->probe = NULL;
- if (code == 0 && list_empty(&drv->driver.klist_devices.k_list))
+ if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
retval = -ENODEV;
drv->driver.probe = platform_drv_probe_fail;
- spin_unlock(&platform_bus_type.klist_drivers.k_lock);
+ spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
if (code != retval)
platform_driver_unregister(drv);
@@ -516,8 +517,8 @@ EXPORT_SYMBOL_GPL(platform_driver_probe);
* (b) sysfs attribute lets new-style coldplug recover from hotplug events
* mishandled before system is fully running: "modprobe $(cat modalias)"
*/
-static ssize_t
-modalias_show(struct device *dev, struct device_attribute *a, char *buf)
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+ char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name);
@@ -538,26 +539,24 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
-
/**
- * platform_match - bind platform device to platform driver.
- * @dev: device.
- * @drv: driver.
+ * platform_match - bind platform device to platform driver.
+ * @dev: device.
+ * @drv: driver.
*
- * Platform device IDs are assumed to be encoded like this:
- * "<name><instance>", where <name> is a short description of the
- * type of device, like "pci" or "floppy", and <instance> is the
- * enumerated instance of the device, like '0' or '42'.
- * Driver IDs are simply "<name>".
- * So, extract the <name> from the platform_device structure,
- * and compare it against the name of the driver. Return whether
- * they match or not.
+ * Platform device IDs are assumed to be encoded like this:
+ * "<name><instance>", where <name> is a short description of the type of
+ * device, like "pci" or "floppy", and <instance> is the enumerated
+ * instance of the device, like '0' or '42'. Driver IDs are simply
+ * "<name>". So, extract the <name> from the platform_device structure,
+ * and compare it against the name of the driver. Return whether they match
+ * or not.
*/
-
-static int platform_match(struct device * dev, struct device_driver * drv)
+static int platform_match(struct device *dev, struct device_driver *drv)
{
- struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ struct platform_device *pdev;
+ pdev = container_of(dev, struct platform_device, dev);
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
@@ -574,9 +573,10 @@ static int platform_suspend(struct device *dev, pm_message_t mesg)
static int platform_suspend_late(struct device *dev, pm_message_t mesg)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
- struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ struct platform_device *pdev;
int ret = 0;
+ pdev = container_of(dev, struct platform_device, dev);
if (dev->driver && drv->suspend_late)
ret = drv->suspend_late(pdev, mesg);
@@ -586,16 +586,17 @@ static int platform_suspend_late(struct device *dev, pm_message_t mesg)
static int platform_resume_early(struct device *dev)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
- struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ struct platform_device *pdev;
int ret = 0;
+ pdev = container_of(dev, struct platform_device, dev);
if (dev->driver && drv->resume_early)
ret = drv->resume_early(pdev);
return ret;
}
-static int platform_resume(struct device * dev)
+static int platform_resume(struct device *dev)
{
int ret = 0;
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 44504e6618fb..06a86fe6a78d 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,3 @@
-obj-y := shutdown.o
obj-$(CONFIG_PM) += sysfs.o
obj-$(CONFIG_PM_SLEEP) += main.o
obj-$(CONFIG_PM_TRACE) += trace.o
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 691ffb64cc37..200ed5fafd50 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -24,20 +24,45 @@
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/resume-trace.h>
+#include <linux/rwsem.h>
#include "../base.h"
#include "power.h"
+/*
+ * The entries in the dpm_active list are in a depth first order, simply
+ * because children are guaranteed to be discovered after parents, and
+ * are inserted at the back of the list on discovery.
+ *
+ * All the other lists are kept in the same order, for consistency.
+ * However the lists aren't always traversed in the same order.
+ * Semaphores must be acquired from the top (i.e., front) down
+ * and released in the opposite order. Devices must be suspended
+ * from the bottom (i.e., end) up and resumed in the opposite order.
+ * That way no parent will be suspended while it still has an active
+ * child.
+ *
+ * Since device_pm_add() may be called with a device semaphore held,
+ * we must never try to acquire a device semaphore while holding
+ * dpm_list_mutex.
+ */
+
LIST_HEAD(dpm_active);
+static LIST_HEAD(dpm_locked);
static LIST_HEAD(dpm_off);
static LIST_HEAD(dpm_off_irq);
+static LIST_HEAD(dpm_destroy);
-static DEFINE_MUTEX(dpm_mtx);
static DEFINE_MUTEX(dpm_list_mtx);
-int (*platform_enable_wakeup)(struct device *dev, int is_on);
+static DECLARE_RWSEM(pm_sleep_rwsem);
+int (*platform_enable_wakeup)(struct device *dev, int is_on);
+/**
+ * device_pm_add - add a device to the list of active devices
+ * @dev: Device to be added to the list
+ */
void device_pm_add(struct device *dev)
{
pr_debug("PM: Adding info for %s:%s\n",
@@ -48,8 +73,36 @@ void device_pm_add(struct device *dev)
mutex_unlock(&dpm_list_mtx);
}
+/**
+ * device_pm_remove - remove a device from the list of active devices
+ * @dev: Device to be removed from the list
+ *
+ * This function also removes the device's PM-related sysfs attributes.
+ */
void device_pm_remove(struct device *dev)
{
+ /*
+ * If this function is called during a suspend, it will be blocked,
+ * because we're holding the device's semaphore at that time, which may
+ * lead to a deadlock. In that case we want to print a warning.
+ * However, it may also be called by unregister_dropped_devices() with
+ * the device's semaphore released, in which case the warning should
+ * not be printed.
+ */
+ if (down_trylock(&dev->sem)) {
+ if (down_read_trylock(&pm_sleep_rwsem)) {
+ /* No suspend in progress, wait on dev->sem */
+ down(&dev->sem);
+ up_read(&pm_sleep_rwsem);
+ } else {
+ /* Suspend in progress, we may deadlock */
+ dev_warn(dev, "Suspicious %s during suspend\n",
+ __FUNCTION__);
+ dump_stack();
+ /* The user has been warned ... */
+ down(&dev->sem);
+ }
+ }
pr_debug("PM: Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj));
@@ -57,25 +110,124 @@ void device_pm_remove(struct device *dev)
dpm_sysfs_remove(dev);
list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
+ up(&dev->sem);
+}
+
+/**
+ * device_pm_schedule_removal - schedule the removal of a suspended device
+ * @dev: Device to destroy
+ *
+ * Moves the device to the dpm_destroy list for further processing by
+ * unregister_dropped_devices().
+ */
+void device_pm_schedule_removal(struct device *dev)
+{
+ pr_debug("PM: Preparing for removal: %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus",
+ kobject_name(&dev->kobj));
+ mutex_lock(&dpm_list_mtx);
+ list_move_tail(&dev->power.entry, &dpm_destroy);
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * pm_sleep_lock - mutual exclusion for registration and suspend
+ *
+ * Returns 0 if no suspend is underway and device registration
+ * may proceed, otherwise -EBUSY.
+ */
+int pm_sleep_lock(void)
+{
+ if (down_read_trylock(&pm_sleep_rwsem))
+ return 0;
+
+ return -EBUSY;
+}
+
+/**
+ * pm_sleep_unlock - mutual exclusion for registration and suspend
+ *
+ * This routine undoes the effect of device_pm_add_lock
+ * when a device's registration is complete.
+ */
+void pm_sleep_unlock(void)
+{
+ up_read(&pm_sleep_rwsem);
}
/*------------------------- Resume routines -------------------------*/
/**
- * resume_device - Restore state for one device.
+ * resume_device_early - Power on one device (early resume).
* @dev: Device.
*
+ * Must be called with interrupts disabled.
*/
-
-static int resume_device(struct device * dev)
+static int resume_device_early(struct device *dev)
{
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
- down(&dev->sem);
+ if (dev->bus && dev->bus->resume_early) {
+ dev_dbg(dev, "EARLY resume\n");
+ error = dev->bus->resume_early(dev);
+ }
+
+ TRACE_RESUME(error);
+ return error;
+}
+
+/**
+ * dpm_power_up - Power on all regular (non-sysdev) devices.
+ *
+ * Walk the dpm_off_irq list and power each device up. This
+ * is used for devices that required they be powered down with
+ * interrupts disabled. As devices are powered on, they are moved
+ * to the dpm_off list.
+ *
+ * Must be called with interrupts disabled and only one CPU running.
+ */
+static void dpm_power_up(void)
+{
+
+ while (!list_empty(&dpm_off_irq)) {
+ struct list_head *entry = dpm_off_irq.next;
+ struct device *dev = to_device(entry);
+
+ list_move_tail(entry, &dpm_off);
+ resume_device_early(dev);
+ }
+}
+
+/**
+ * device_power_up - Turn on all devices that need special attention.
+ *
+ * Power on system devices, then devices that required we shut them down
+ * with interrupts disabled.
+ *
+ * Must be called with interrupts disabled.
+ */
+void device_power_up(void)
+{
+ sysdev_resume();
+ dpm_power_up();
+}
+EXPORT_SYMBOL_GPL(device_power_up);
+
+/**
+ * resume_device - Restore state for one device.
+ * @dev: Device.
+ *
+ */
+static int resume_device(struct device *dev)
+{
+ int error = 0;
+
+ TRACE_DEVICE(dev);
+ TRACE_RESUME(0);
if (dev->bus && dev->bus->resume) {
dev_dbg(dev,"resuming\n");
@@ -92,126 +244,94 @@ static int resume_device(struct device * dev)
error = dev->class->resume(dev);
}
- up(&dev->sem);
-
TRACE_RESUME(error);
return error;
}
-
-static int resume_device_early(struct device * dev)
-{
- int error = 0;
-
- TRACE_DEVICE(dev);
- TRACE_RESUME(0);
- if (dev->bus && dev->bus->resume_early) {
- dev_dbg(dev,"EARLY resume\n");
- error = dev->bus->resume_early(dev);
- }
- TRACE_RESUME(error);
- return error;
-}
-
-/*
- * Resume the devices that have either not gone through
- * the late suspend, or that did go through it but also
- * went through the early resume
+/**
+ * dpm_resume - Resume every device.
+ *
+ * Resume the devices that have either not gone through
+ * the late suspend, or that did go through it but also
+ * went through the early resume.
+ *
+ * Take devices from the dpm_off_list, resume them,
+ * and put them on the dpm_locked list.
*/
static void dpm_resume(void)
{
mutex_lock(&dpm_list_mtx);
while(!list_empty(&dpm_off)) {
- struct list_head * entry = dpm_off.next;
- struct device * dev = to_device(entry);
-
- get_device(dev);
- list_move_tail(entry, &dpm_active);
+ struct list_head *entry = dpm_off.next;
+ struct device *dev = to_device(entry);
+ list_move_tail(entry, &dpm_locked);
mutex_unlock(&dpm_list_mtx);
resume_device(dev);
mutex_lock(&dpm_list_mtx);
- put_device(dev);
}
mutex_unlock(&dpm_list_mtx);
}
-
/**
- * device_resume - Restore state of each device in system.
+ * unlock_all_devices - Release each device's semaphore
*
- * Walk the dpm_off list, remove each entry, resume the device,
- * then add it to the dpm_active list.
+ * Go through the dpm_off list. Put each device on the dpm_active
+ * list and unlock it.
*/
-
-void device_resume(void)
+static void unlock_all_devices(void)
{
- might_sleep();
- mutex_lock(&dpm_mtx);
- dpm_resume();
- mutex_unlock(&dpm_mtx);
-}
-
-EXPORT_SYMBOL_GPL(device_resume);
+ mutex_lock(&dpm_list_mtx);
+ while (!list_empty(&dpm_locked)) {
+ struct list_head *entry = dpm_locked.prev;
+ struct device *dev = to_device(entry);
+ list_move(entry, &dpm_active);
+ up(&dev->sem);
+ }
+ mutex_unlock(&dpm_list_mtx);
+}
/**
- * dpm_power_up - Power on some devices.
- *
- * Walk the dpm_off_irq list and power each device up. This
- * is used for devices that required they be powered down with
- * interrupts disabled. As devices are powered on, they are moved
- * to the dpm_active list.
+ * unregister_dropped_devices - Unregister devices scheduled for removal
*
- * Interrupts must be disabled when calling this.
+ * Unregister all devices on the dpm_destroy list.
*/
-
-static void dpm_power_up(void)
+static void unregister_dropped_devices(void)
{
- while(!list_empty(&dpm_off_irq)) {
- struct list_head * entry = dpm_off_irq.next;
- struct device * dev = to_device(entry);
+ mutex_lock(&dpm_list_mtx);
+ while (!list_empty(&dpm_destroy)) {
+ struct list_head *entry = dpm_destroy.next;
+ struct device *dev = to_device(entry);
- list_move_tail(entry, &dpm_off);
- resume_device_early(dev);
+ up(&dev->sem);
+ mutex_unlock(&dpm_list_mtx);
+ /* This also removes the device from the list */
+ device_unregister(dev);
+ mutex_lock(&dpm_list_mtx);
}
+ mutex_unlock(&dpm_list_mtx);
}
-
/**
- * device_power_up - Turn on all devices that need special attention.
+ * device_resume - Restore state of each device in system.
*
- * Power on system devices then devices that required we shut them down
- * with interrupts disabled.
- * Called with interrupts disabled.
+ * Resume all the devices, unlock them all, and allow new
+ * devices to be registered once again.
*/
-
-void device_power_up(void)
+void device_resume(void)
{
- sysdev_resume();
- dpm_power_up();
+ might_sleep();
+ dpm_resume();
+ unlock_all_devices();
+ unregister_dropped_devices();
+ up_write(&pm_sleep_rwsem);
}
-
-EXPORT_SYMBOL_GPL(device_power_up);
+EXPORT_SYMBOL_GPL(device_resume);
/*------------------------- Suspend routines -------------------------*/
-/*
- * The entries in the dpm_active list are in a depth first order, simply
- * because children are guaranteed to be discovered after parents, and
- * are inserted at the back of the list on discovery.
- *
- * All list on the suspend path are done in reverse order, so we operate
- * on the leaves of the device tree (or forests, depending on how you want
- * to look at it ;) first. As nodes are removed from the back of the list,
- * they are inserted into the front of their destintation lists.
- *
- * Things are the reverse on the resume path - iterations are done in
- * forward order, and nodes are inserted at the back of their destination
- * lists. This way, the ancestors will be accessed before their descendents.
- */
-
static inline char *suspend_verb(u32 event)
{
switch (event) {
@@ -222,7 +342,6 @@ static inline char *suspend_verb(u32 event)
}
}
-
static void
suspend_device_dbg(struct device *dev, pm_message_t state, char *info)
{
@@ -232,16 +351,73 @@ suspend_device_dbg(struct device *dev, pm_message_t state, char *info)
}
/**
- * suspend_device - Save state of one device.
+ * suspend_device_late - Shut down one device (late suspend).
* @dev: Device.
* @state: Power state device is entering.
+ *
+ * This is called with interrupts off and only a single CPU running.
*/
+static int suspend_device_late(struct device *dev, pm_message_t state)
+{
+ int error = 0;
-static int suspend_device(struct device * dev, pm_message_t state)
+ if (dev->bus && dev->bus->suspend_late) {
+ suspend_device_dbg(dev, state, "LATE ");
+ error = dev->bus->suspend_late(dev, state);
+ suspend_report_result(dev->bus->suspend_late, error);
+ }
+ return error;
+}
+
+/**
+ * device_power_down - Shut down special devices.
+ * @state: Power state to enter.
+ *
+ * Power down devices that require interrupts to be disabled
+ * and move them from the dpm_off list to the dpm_off_irq list.
+ * Then power down system devices.
+ *
+ * Must be called with interrupts disabled and only one CPU running.
+ */
+int device_power_down(pm_message_t state)
+{
+ int error = 0;
+
+ while (!list_empty(&dpm_off)) {
+ struct list_head *entry = dpm_off.prev;
+ struct device *dev = to_device(entry);
+
+ list_del_init(&dev->power.entry);
+ error = suspend_device_late(dev, state);
+ if (error) {
+ printk(KERN_ERR "Could not power down device %s: "
+ "error %d\n",
+ kobject_name(&dev->kobj), error);
+ if (list_empty(&dev->power.entry))
+ list_add(&dev->power.entry, &dpm_off);
+ break;
+ }
+ if (list_empty(&dev->power.entry))
+ list_add(&dev->power.entry, &dpm_off_irq);
+ }
+
+ if (!error)
+ error = sysdev_suspend(state);
+ if (error)
+ dpm_power_up();
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_power_down);
+
+/**
+ * suspend_device - Save state of one device.
+ * @dev: Device.
+ * @state: Power state device is entering.
+ */
+int suspend_device(struct device *dev, pm_message_t state)
{
int error = 0;
- down(&dev->sem);
if (dev->power.power_state.event) {
dev_dbg(dev, "PM: suspend %d-->%d\n",
dev->power.power_state.event, state.event);
@@ -264,123 +440,105 @@ static int suspend_device(struct device * dev, pm_message_t state)
error = dev->bus->suspend(dev, state);
suspend_report_result(dev->bus->suspend, error);
}
- up(&dev->sem);
- return error;
-}
-
-
-/*
- * This is called with interrupts off, only a single CPU
- * running. We can't acquire a mutex or semaphore (and we don't
- * need the protection)
- */
-static int suspend_device_late(struct device *dev, pm_message_t state)
-{
- int error = 0;
-
- if (dev->bus && dev->bus->suspend_late) {
- suspend_device_dbg(dev, state, "LATE ");
- error = dev->bus->suspend_late(dev, state);
- suspend_report_result(dev->bus->suspend_late, error);
- }
return error;
}
/**
- * device_suspend - Save state and stop all devices in system.
- * @state: Power state to put each device in.
+ * dpm_suspend - Suspend every device.
+ * @state: Power state to put each device in.
*
- * Walk the dpm_active list, call ->suspend() for each device, and move
- * it to the dpm_off list.
+ * Walk the dpm_locked list. Suspend each device and move it
+ * to the dpm_off list.
*
* (For historical reasons, if it returns -EAGAIN, that used to mean
* that the device would be called again with interrupts disabled.
* These days, we use the "suspend_late()" callback for that, so we
* print a warning and consider it an error).
- *
- * If we get a different error, try and back out.
- *
- * If we hit a failure with any of the devices, call device_resume()
- * above to bring the suspended devices back to life.
- *
*/
-
-int device_suspend(pm_message_t state)
+static int dpm_suspend(pm_message_t state)
{
int error = 0;
- might_sleep();
- mutex_lock(&dpm_mtx);
mutex_lock(&dpm_list_mtx);
- while (!list_empty(&dpm_active) && error == 0) {
- struct list_head * entry = dpm_active.prev;
- struct device * dev = to_device(entry);
+ while (!list_empty(&dpm_locked)) {
+ struct list_head *entry = dpm_locked.prev;
+ struct device *dev = to_device(entry);
- get_device(dev);
+ list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
-
error = suspend_device(dev, state);
-
- mutex_lock(&dpm_list_mtx);
-
- /* Check if the device got removed */
- if (!list_empty(&dev->power.entry)) {
- /* Move it to the dpm_off list */
- if (!error)
- list_move(&dev->power.entry, &dpm_off);
- }
- if (error)
+ if (error) {
printk(KERN_ERR "Could not suspend device %s: "
- "error %d%s\n",
- kobject_name(&dev->kobj), error,
- error == -EAGAIN ? " (please convert to suspend_late)" : "");
- put_device(dev);
+ "error %d%s\n",
+ kobject_name(&dev->kobj),
+ error,
+ (error == -EAGAIN ?
+ " (please convert to suspend_late)" :
+ ""));
+ mutex_lock(&dpm_list_mtx);
+ if (list_empty(&dev->power.entry))
+ list_add(&dev->power.entry, &dpm_locked);
+ mutex_unlock(&dpm_list_mtx);
+ break;
+ }
+ mutex_lock(&dpm_list_mtx);
+ if (list_empty(&dev->power.entry))
+ list_add(&dev->power.entry, &dpm_off);
}
mutex_unlock(&dpm_list_mtx);
- if (error)
- dpm_resume();
- mutex_unlock(&dpm_mtx);
return error;
}
-EXPORT_SYMBOL_GPL(device_suspend);
-
/**
- * device_power_down - Shut down special devices.
- * @state: Power state to enter.
+ * lock_all_devices - Acquire every device's semaphore
*
- * Walk the dpm_off_irq list, calling ->power_down() for each device that
- * couldn't power down the device with interrupts enabled. When we're
- * done, power down system devices.
+ * Go through the dpm_active list. Carefully lock each device's
+ * semaphore and put it in on the dpm_locked list.
*/
-
-int device_power_down(pm_message_t state)
+static void lock_all_devices(void)
{
- int error = 0;
- struct device * dev;
+ mutex_lock(&dpm_list_mtx);
+ while (!list_empty(&dpm_active)) {
+ struct list_head *entry = dpm_active.next;
+ struct device *dev = to_device(entry);
- while (!list_empty(&dpm_off)) {
- struct list_head * entry = dpm_off.prev;
+ /* Required locking order is dev->sem first,
+ * then dpm_list_mutex. Hence this awkward code.
+ */
+ get_device(dev);
+ mutex_unlock(&dpm_list_mtx);
+ down(&dev->sem);
+ mutex_lock(&dpm_list_mtx);
- dev = to_device(entry);
- error = suspend_device_late(dev, state);
- if (error)
- goto Error;
- list_move(&dev->power.entry, &dpm_off_irq);
+ if (list_empty(entry))
+ up(&dev->sem); /* Device was removed */
+ else
+ list_move_tail(entry, &dpm_locked);
+ put_device(dev);
}
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * device_suspend - Save state and stop all devices in system.
+ *
+ * Prevent new devices from being registered, then lock all devices
+ * and suspend them.
+ */
+int device_suspend(pm_message_t state)
+{
+ int error;
- error = sysdev_suspend(state);
- Done:
+ might_sleep();
+ down_write(&pm_sleep_rwsem);
+ lock_all_devices();
+ error = dpm_suspend(state);
+ if (error)
+ device_resume();
return error;
- Error:
- printk(KERN_ERR "Could not power down device %s: "
- "error %d\n", kobject_name(&dev->kobj), error);
- dpm_power_up();
- goto Done;
}
-
-EXPORT_SYMBOL_GPL(device_power_down);
+EXPORT_SYMBOL_GPL(device_suspend);
void __suspend_report_result(const char *function, void *fn, int ret)
{
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 379da4e958e0..6f0dfca8ebdd 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -1,10 +1,3 @@
-/*
- * shutdown.c
- */
-
-extern void device_shutdown(void);
-
-
#ifdef CONFIG_PM_SLEEP
/*
@@ -20,6 +13,9 @@ static inline struct device *to_device(struct list_head *entry)
extern void device_pm_add(struct device *);
extern void device_pm_remove(struct device *);
+extern void device_pm_schedule_removal(struct device *);
+extern int pm_sleep_lock(void);
+extern void pm_sleep_unlock(void);
#else /* CONFIG_PM_SLEEP */
@@ -32,6 +28,15 @@ static inline void device_pm_remove(struct device *dev)
{
}
+static inline int pm_sleep_lock(void)
+{
+ return 0;
+}
+
+static inline void pm_sleep_unlock(void)
+{
+}
+
#endif
#ifdef CONFIG_PM
diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c
deleted file mode 100644
index 56e8eaaac012..000000000000
--- a/drivers/base/power/shutdown.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * shutdown.c - power management functions for the device tree.
- *
- * Copyright (c) 2002-3 Patrick Mochel
- * 2002-3 Open Source Development Lab
- *
- * This file is released under the GPLv2
- *
- */
-
-#include <linux/device.h>
-#include <asm/semaphore.h>
-
-#include "../base.h"
-#include "power.h"
-
-#define to_dev(node) container_of(node, struct device, kobj.entry)
-
-
-/**
- * We handle system devices differently - we suspend and shut them
- * down last and resume them first. That way, we don't do anything stupid like
- * shutting down the interrupt controller before any devices..
- *
- * Note that there are not different stages for power management calls -
- * they only get one called once when interrupts are disabled.
- */
-
-
-/**
- * device_shutdown - call ->shutdown() on each device to shutdown.
- */
-void device_shutdown(void)
-{
- struct device * dev, *devn;
-
- list_for_each_entry_safe_reverse(dev, devn, &devices_subsys.list,
- kobj.entry) {
- if (dev->bus && dev->bus->shutdown) {
- dev_dbg(dev, "shutdown\n");
- dev->bus->shutdown(dev);
- } else if (dev->driver && dev->driver->shutdown) {
- dev_dbg(dev, "shutdown\n");
- dev->driver->shutdown(dev);
- }
- }
-}
-
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index ac7ff6d0c6e5..2f79c55acdcc 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -25,8 +25,6 @@
#include "base.h"
-extern struct kset devices_subsys;
-
#define to_sysdev(k) container_of(k, struct sys_device, kobj)
#define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
@@ -128,18 +126,17 @@ void sysdev_class_remove_file(struct sysdev_class *c,
}
EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
-/*
- * declare system_subsys
- */
-static decl_subsys(system, &ktype_sysdev_class, NULL);
+static struct kset *system_kset;
int sysdev_class_register(struct sysdev_class * cls)
{
pr_debug("Registering sysdev class '%s'\n",
kobject_name(&cls->kset.kobj));
INIT_LIST_HEAD(&cls->drivers);
- cls->kset.kobj.parent = &system_subsys.kobj;
- cls->kset.kobj.kset = &system_subsys;
+ cls->kset.kobj.parent = &system_kset->kobj;
+ cls->kset.kobj.ktype = &ktype_sysdev_class;
+ cls->kset.kobj.kset = system_kset;
+ kobject_set_name(&cls->kset.kobj, cls->name);
return kset_register(&cls->kset);
}
@@ -228,20 +225,15 @@ int sysdev_register(struct sys_device * sysdev)
if (!cls)
return -EINVAL;
+ pr_debug("Registering sys device '%s'\n", kobject_name(&sysdev->kobj));
+
/* Make sure the kset is set */
sysdev->kobj.kset = &cls->kset;
- /* But make sure we point to the right type for sysfs translation */
- sysdev->kobj.ktype = &ktype_sysdev;
- error = kobject_set_name(&sysdev->kobj, "%s%d",
- kobject_name(&cls->kset.kobj), sysdev->id);
- if (error)
- return error;
-
- pr_debug("Registering sys device '%s'\n", kobject_name(&sysdev->kobj));
-
/* Register the object */
- error = kobject_register(&sysdev->kobj);
+ error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
+ "%s%d", kobject_name(&cls->kset.kobj),
+ sysdev->id);
if (!error) {
struct sysdev_driver * drv;
@@ -258,6 +250,7 @@ int sysdev_register(struct sys_device * sysdev)
}
mutex_unlock(&sysdev_drivers_lock);
}
+ kobject_uevent(&sysdev->kobj, KOBJ_ADD);
return error;
}
@@ -272,7 +265,7 @@ void sysdev_unregister(struct sys_device * sysdev)
}
mutex_unlock(&sysdev_drivers_lock);
- kobject_unregister(&sysdev->kobj);
+ kobject_put(&sysdev->kobj);
}
@@ -298,8 +291,7 @@ void sysdev_shutdown(void)
pr_debug("Shutting Down System Devices\n");
mutex_lock(&sysdev_drivers_lock);
- list_for_each_entry_reverse(cls, &system_subsys.list,
- kset.kobj.entry) {
+ list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
struct sys_device * sysdev;
pr_debug("Shutting down type '%s':\n",
@@ -361,9 +353,7 @@ int sysdev_suspend(pm_message_t state)
pr_debug("Suspending System Devices\n");
- list_for_each_entry_reverse(cls, &system_subsys.list,
- kset.kobj.entry) {
-
+ list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
pr_debug("Suspending type '%s':\n",
kobject_name(&cls->kset.kobj));
@@ -414,8 +404,7 @@ aux_driver:
}
/* resume other classes */
- list_for_each_entry_continue(cls, &system_subsys.list,
- kset.kobj.entry) {
+ list_for_each_entry_continue(cls, &system_kset->list, kset.kobj.entry) {
list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
pr_debug(" %s\n", kobject_name(&err_dev->kobj));
__sysdev_resume(err_dev);
@@ -440,7 +429,7 @@ int sysdev_resume(void)
pr_debug("Resuming System Devices\n");
- list_for_each_entry(cls, &system_subsys.list, kset.kobj.entry) {
+ list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) {
struct sys_device * sysdev;
pr_debug("Resuming type '%s':\n",
@@ -458,8 +447,10 @@ int sysdev_resume(void)
int __init system_bus_init(void)
{
- system_subsys.kobj.parent = &devices_subsys.kobj;
- return subsystem_register(&system_subsys);
+ system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
+ if (!system_kset)
+ return -ENOMEM;
+ return 0;
}
EXPORT_SYMBOL_GPL(sysdev_register);
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index ad00b3d94711..826d12381e21 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -15,8 +15,10 @@
static struct kmem_cache *buf_pool_cache;
-static ssize_t aoedisk_show_state(struct gendisk * disk, char *page)
+static ssize_t aoedisk_show_state(struct device *dev,
+ struct device_attribute *attr, char *page)
{
+ struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE,
@@ -26,50 +28,47 @@ static ssize_t aoedisk_show_state(struct gendisk * disk, char *page)
(d->nopen && !(d->flags & DEVFL_UP)) ? ",closewait" : "");
/* I'd rather see nopen exported so we can ditch closewait */
}
-static ssize_t aoedisk_show_mac(struct gendisk * disk, char *page)
+static ssize_t aoedisk_show_mac(struct device *dev,
+ struct device_attribute *attr, char *page)
{
+ struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE, "%012llx\n",
(unsigned long long)mac_addr(d->addr));
}
-static ssize_t aoedisk_show_netif(struct gendisk * disk, char *page)
+static ssize_t aoedisk_show_netif(struct device *dev,
+ struct device_attribute *attr, char *page)
{
+ struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE, "%s\n", d->ifp->name);
}
/* firmware version */
-static ssize_t aoedisk_show_fwver(struct gendisk * disk, char *page)
+static ssize_t aoedisk_show_fwver(struct device *dev,
+ struct device_attribute *attr, char *page)
{
+ struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE, "0x%04x\n", (unsigned int) d->fw_ver);
}
-static struct disk_attribute disk_attr_state = {
- .attr = {.name = "state", .mode = S_IRUGO },
- .show = aoedisk_show_state
-};
-static struct disk_attribute disk_attr_mac = {
- .attr = {.name = "mac", .mode = S_IRUGO },
- .show = aoedisk_show_mac
-};
-static struct disk_attribute disk_attr_netif = {
- .attr = {.name = "netif", .mode = S_IRUGO },
- .show = aoedisk_show_netif
-};
-static struct disk_attribute disk_attr_fwver = {
- .attr = {.name = "firmware-version", .mode = S_IRUGO },
- .show = aoedisk_show_fwver
+static DEVICE_ATTR(state, S_IRUGO, aoedisk_show_state, NULL);
+static DEVICE_ATTR(mac, S_IRUGO, aoedisk_show_mac, NULL);
+static DEVICE_ATTR(netif, S_IRUGO, aoedisk_show_netif, NULL);
+static struct device_attribute dev_attr_firmware_version = {
+ .attr = { .name = "firmware-version", .mode = S_IRUGO, .owner = THIS_MODULE },
+ .show = aoedisk_show_fwver,
};
static struct attribute *aoe_attrs[] = {
- &disk_attr_state.attr,
- &disk_attr_mac.attr,
- &disk_attr_netif.attr,
- &disk_attr_fwver.attr,
- NULL
+ &dev_attr_state.attr,
+ &dev_attr_mac.attr,
+ &dev_attr_netif.attr,
+ &dev_attr_firmware_version.attr,
+ NULL,
};
static const struct attribute_group attr_group = {
@@ -79,12 +78,12 @@ static const struct attribute_group attr_group = {
static int
aoedisk_add_sysfs(struct aoedev *d)
{
- return sysfs_create_group(&d->gd->kobj, &attr_group);
+ return sysfs_create_group(&d->gd->dev.kobj, &attr_group);
}
void
aoedisk_rm_sysfs(struct aoedev *d)
{
- sysfs_remove_group(&d->gd->kobj, &attr_group);
+ sysfs_remove_group(&d->gd->dev.kobj, &attr_group);
}
static int
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c
index 39e563ea0878..d5480e34cb22 100644
--- a/drivers/block/aoe/aoechr.c
+++ b/drivers/block/aoe/aoechr.c
@@ -259,9 +259,8 @@ aoechr_init(void)
return PTR_ERR(aoe_class);
}
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
- class_device_create(aoe_class, NULL,
- MKDEV(AOE_MAJOR, chardevs[i].minor),
- NULL, chardevs[i].name);
+ device_create(aoe_class, NULL,
+ MKDEV(AOE_MAJOR, chardevs[i].minor), chardevs[i].name);
return 0;
}
@@ -272,7 +271,7 @@ aoechr_exit(void)
int i;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
- class_device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
+ device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
class_destroy(aoe_class);
unregister_chrdev(AOE_MAJOR, "aoechr");
}
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index b4c0888aedc3..ba9b17e507e0 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -375,14 +375,17 @@ harderror:
return NULL;
}
-static ssize_t pid_show(struct gendisk *disk, char *page)
+static ssize_t pid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return sprintf(page, "%ld\n",
+ struct gendisk *disk = dev_to_disk(dev);
+
+ return sprintf(buf, "%ld\n",
(long) ((struct nbd_device *)disk->private_data)->pid);
}
-static struct disk_attribute pid_attr = {
- .attr = { .name = "pid", .mode = S_IRUGO },
+static struct device_attribute pid_attr = {
+ .attr = { .name = "pid", .mode = S_IRUGO, .owner = THIS_MODULE },
.show = pid_show,
};
@@ -394,7 +397,7 @@ static int nbd_do_it(struct nbd_device *lo)
BUG_ON(lo->magic != LO_MAGIC);
lo->pid = current->pid;
- ret = sysfs_create_file(&lo->disk->kobj, &pid_attr.attr);
+ ret = sysfs_create_file(&lo->disk->dev.kobj, &pid_attr.attr);
if (ret) {
printk(KERN_ERR "nbd: sysfs_create_file failed!");
return ret;
@@ -403,7 +406,7 @@ static int nbd_do_it(struct nbd_device *lo)
while ((req = nbd_read_stat(lo)) != NULL)
nbd_end_request(req);
- sysfs_remove_file(&lo->disk->kobj, &pid_attr.attr);
+ sysfs_remove_file(&lo->disk->dev.kobj, &pid_attr.attr);
return 0;
}
diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c
index d89e7d32a3b6..ab86e23ddc69 100644
--- a/drivers/block/paride/pg.c
+++ b/drivers/block/paride/pg.c
@@ -676,8 +676,8 @@ static int __init pg_init(void)
for (unit = 0; unit < PG_UNITS; unit++) {
struct pg *dev = &devices[unit];
if (dev->present)
- class_device_create(pg_class, NULL, MKDEV(major, unit),
- NULL, "pg%u", unit);
+ device_create(pg_class, NULL, MKDEV(major, unit),
+ "pg%u", unit);
}
err = 0;
goto out;
@@ -695,7 +695,7 @@ static void __exit pg_exit(void)
for (unit = 0; unit < PG_UNITS; unit++) {
struct pg *dev = &devices[unit];
if (dev->present)
- class_device_destroy(pg_class, MKDEV(major, unit));
+ device_destroy(pg_class, MKDEV(major, unit));
}
class_destroy(pg_class);
unregister_chrdev(major, name);
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
index b91accf12656..76096cad798f 100644
--- a/drivers/block/paride/pt.c
+++ b/drivers/block/paride/pt.c
@@ -972,10 +972,10 @@ static int __init pt_init(void)
for (unit = 0; unit < PT_UNITS; unit++)
if (pt[unit].present) {
- class_device_create(pt_class, NULL, MKDEV(major, unit),
- NULL, "pt%d", unit);
- class_device_create(pt_class, NULL, MKDEV(major, unit + 128),
- NULL, "pt%dn", unit);
+ device_create(pt_class, NULL, MKDEV(major, unit),
+ "pt%d", unit);
+ device_create(pt_class, NULL, MKDEV(major, unit + 128),
+ "pt%dn", unit);
}
goto out;
@@ -990,8 +990,8 @@ static void __exit pt_exit(void)
int unit;
for (unit = 0; unit < PT_UNITS; unit++)
if (pt[unit].present) {
- class_device_destroy(pt_class, MKDEV(major, unit));
- class_device_destroy(pt_class, MKDEV(major, unit + 128));
+ device_destroy(pt_class, MKDEV(major, unit));
+ device_destroy(pt_class, MKDEV(major, unit + 128));
}
class_destroy(pt_class);
unregister_chrdev(major, name);
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 3535ef896677..e9de1712e5a0 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -110,17 +110,18 @@ static struct pktcdvd_kobj* pkt_kobj_create(struct pktcdvd_device *pd,
struct kobj_type* ktype)
{
struct pktcdvd_kobj *p;
+ int error;
+
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return NULL;
- kobject_set_name(&p->kobj, "%s", name);
- p->kobj.parent = parent;
- p->kobj.ktype = ktype;
p->pd = pd;
- if (kobject_register(&p->kobj) != 0) {
+ error = kobject_init_and_add(&p->kobj, ktype, parent, "%s", name);
+ if (error) {
kobject_put(&p->kobj);
return NULL;
}
+ kobject_uevent(&p->kobj, KOBJ_ADD);
return p;
}
/*
@@ -129,7 +130,7 @@ static struct pktcdvd_kobj* pkt_kobj_create(struct pktcdvd_device *pd,
static void pkt_kobj_remove(struct pktcdvd_kobj *p)
{
if (p)
- kobject_unregister(&p->kobj);
+ kobject_put(&p->kobj);
}
/*
* default release function for pktcdvd kernel objects.
@@ -301,18 +302,16 @@ static struct kobj_type kobj_pkt_type_wqueue = {
static void pkt_sysfs_dev_new(struct pktcdvd_device *pd)
{
if (class_pktcdvd) {
- pd->clsdev = class_device_create(class_pktcdvd,
- NULL, pd->pkt_dev,
- NULL, "%s", pd->name);
- if (IS_ERR(pd->clsdev))
- pd->clsdev = NULL;
+ pd->dev = device_create(class_pktcdvd, NULL, pd->pkt_dev, "%s", pd->name);
+ if (IS_ERR(pd->dev))
+ pd->dev = NULL;
}
- if (pd->clsdev) {
+ if (pd->dev) {
pd->kobj_stat = pkt_kobj_create(pd, "stat",
- &pd->clsdev->kobj,
+ &pd->dev->kobj,
&kobj_pkt_type_stat);
pd->kobj_wqueue = pkt_kobj_create(pd, "write_queue",
- &pd->clsdev->kobj,
+ &pd->dev->kobj,
&kobj_pkt_type_wqueue);
}
}
@@ -322,7 +321,7 @@ static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd)
pkt_kobj_remove(pd->kobj_stat);
pkt_kobj_remove(pd->kobj_wqueue);
if (class_pktcdvd)
- class_device_destroy(class_pktcdvd, pd->pkt_dev);
+ device_destroy(class_pktcdvd, pd->pkt_dev);
}
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 2e3a0d4bc4c2..466629594776 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -373,6 +373,16 @@ config ISTALLION
To compile this driver as a module, choose M here: the
module will be called istallion.
+config NOZOMI
+ tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
+ depends on PCI && EXPERIMENTAL
+ help
+ If you have a HSDPA driver Broadband Wireless Data Card -
+ Globe Trotter PCMCIA card, say Y here.
+
+ To compile this driver as a module, choose M here, the module
+ will be called nozomi.
+
config A2232
tristate "Commodore A2232 serial support (EXPERIMENTAL)"
depends on EXPERIMENTAL && ZORRO && BROKEN_ON_SMP
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 07304d50e0cb..96fc01eddefe 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_SERIAL167) += serial167.o
obj-$(CONFIG_CYCLADES) += cyclades.o
obj-$(CONFIG_STALLION) += stallion.o
obj-$(CONFIG_ISTALLION) += istallion.o
+obj-$(CONFIG_NOZOMI) += nozomi.o
obj-$(CONFIG_DIGIEPCA) += epca.o
obj-$(CONFIG_SPECIALIX) += specialix.o
obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 8252f8668538..480fae29c9b2 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -27,7 +27,7 @@
#include <linux/init.h>
#include <linux/kbd_kern.h>
#include <linux/kernel.h>
-#include <linux/kobject.h>
+#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -89,7 +89,7 @@ struct hvc_struct {
int irq_requested;
int irq;
struct list_head next;
- struct kobject kobj; /* ref count & hvc_struct lifetime */
+ struct kref kref; /* ref count & hvc_struct lifetime */
};
/* dynamic list of hvc_struct instances */
@@ -110,7 +110,7 @@ static int last_hvc = -1;
/*
* Do not call this function with either the hvc_structs_lock or the hvc_struct
- * lock held. If successful, this function increments the kobject reference
+ * lock held. If successful, this function increments the kref reference
* count against the target hvc_struct so it should be released when finished.
*/
static struct hvc_struct *hvc_get_by_index(int index)
@@ -123,7 +123,7 @@ static struct hvc_struct *hvc_get_by_index(int index)
list_for_each_entry(hp, &hvc_structs, next) {
spin_lock_irqsave(&hp->lock, flags);
if (hp->index == index) {
- kobject_get(&hp->kobj);
+ kref_get(&hp->kref);
spin_unlock_irqrestore(&hp->lock, flags);
spin_unlock(&hvc_structs_lock);
return hp;
@@ -242,6 +242,23 @@ static int __init hvc_console_init(void)
}
console_initcall(hvc_console_init);
+/* callback when the kboject ref count reaches zero. */
+static void destroy_hvc_struct(struct kref *kref)
+{
+ struct hvc_struct *hp = container_of(kref, struct hvc_struct, kref);
+ unsigned long flags;
+
+ spin_lock(&hvc_structs_lock);
+
+ spin_lock_irqsave(&hp->lock, flags);
+ list_del(&(hp->next));
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ spin_unlock(&hvc_structs_lock);
+
+ kfree(hp);
+}
+
/*
* hvc_instantiate() is an early console discovery method which locates
* consoles * prior to the vio subsystem discovering them. Hotplugged
@@ -261,7 +278,7 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
/* make sure no no tty has been registered in this index */
hp = hvc_get_by_index(index);
if (hp) {
- kobject_put(&hp->kobj);
+ kref_put(&hp->kref, destroy_hvc_struct);
return -1;
}
@@ -318,9 +335,8 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
unsigned long flags;
int irq = 0;
int rc = 0;
- struct kobject *kobjp;
- /* Auto increments kobject reference if found. */
+ /* Auto increments kref reference if found. */
if (!(hp = hvc_get_by_index(tty->index)))
return -ENODEV;
@@ -341,8 +357,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
if (irq)
hp->irq_requested = 1;
- kobjp = &hp->kobj;
-
spin_unlock_irqrestore(&hp->lock, flags);
/* check error, fallback to non-irq */
if (irq)
@@ -352,7 +366,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
* If the request_irq() fails and we return an error. The tty layer
* will call hvc_close() after a failed open but we don't want to clean
* up there so we'll clean up here and clear out the previously set
- * tty fields and return the kobject reference.
+ * tty fields and return the kref reference.
*/
if (rc) {
spin_lock_irqsave(&hp->lock, flags);
@@ -360,7 +374,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
hp->irq_requested = 0;
spin_unlock_irqrestore(&hp->lock, flags);
tty->driver_data = NULL;
- kobject_put(kobjp);
+ kref_put(&hp->kref, destroy_hvc_struct);
printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
}
/* Force wakeup of the polling thread */
@@ -372,7 +386,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
static void hvc_close(struct tty_struct *tty, struct file * filp)
{
struct hvc_struct *hp;
- struct kobject *kobjp;
int irq = 0;
unsigned long flags;
@@ -382,7 +395,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
/*
* No driver_data means that this close was issued after a failed
* hvc_open by the tty layer's release_dev() function and we can just
- * exit cleanly because the kobject reference wasn't made.
+ * exit cleanly because the kref reference wasn't made.
*/
if (!tty->driver_data)
return;
@@ -390,7 +403,6 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
hp = tty->driver_data;
spin_lock_irqsave(&hp->lock, flags);
- kobjp = &hp->kobj;
if (--hp->count == 0) {
if (hp->irq_requested)
irq = hp->irq;
@@ -417,7 +429,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
spin_unlock_irqrestore(&hp->lock, flags);
}
- kobject_put(kobjp);
+ kref_put(&hp->kref, destroy_hvc_struct);
}
static void hvc_hangup(struct tty_struct *tty)
@@ -426,7 +438,6 @@ static void hvc_hangup(struct tty_struct *tty)
unsigned long flags;
int irq = 0;
int temp_open_count;
- struct kobject *kobjp;
if (!hp)
return;
@@ -443,7 +454,6 @@ static void hvc_hangup(struct tty_struct *tty)
return;
}
- kobjp = &hp->kobj;
temp_open_count = hp->count;
hp->count = 0;
hp->n_outbuf = 0;
@@ -457,7 +467,7 @@ static void hvc_hangup(struct tty_struct *tty)
free_irq(irq, hp);
while(temp_open_count) {
--temp_open_count;
- kobject_put(kobjp);
+ kref_put(&hp->kref, destroy_hvc_struct);
}
}
@@ -729,27 +739,6 @@ static const struct tty_operations hvc_ops = {
.chars_in_buffer = hvc_chars_in_buffer,
};
-/* callback when the kboject ref count reaches zero. */
-static void destroy_hvc_struct(struct kobject *kobj)
-{
- struct hvc_struct *hp = container_of(kobj, struct hvc_struct, kobj);
- unsigned long flags;
-
- spin_lock(&hvc_structs_lock);
-
- spin_lock_irqsave(&hp->lock, flags);
- list_del(&(hp->next));
- spin_unlock_irqrestore(&hp->lock, flags);
-
- spin_unlock(&hvc_structs_lock);
-
- kfree(hp);
-}
-
-static struct kobj_type hvc_kobj_type = {
- .release = destroy_hvc_struct,
-};
-
struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
struct hv_ops *ops, int outbuf_size)
{
@@ -776,8 +765,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
hp->outbuf_size = outbuf_size;
hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];
- kobject_init(&hp->kobj);
- hp->kobj.ktype = &hvc_kobj_type;
+ kref_init(&hp->kref);
spin_lock_init(&hp->lock);
spin_lock(&hvc_structs_lock);
@@ -806,12 +794,10 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
int __devexit hvc_remove(struct hvc_struct *hp)
{
unsigned long flags;
- struct kobject *kobjp;
struct tty_struct *tty;
spin_lock_irqsave(&hp->lock, flags);
tty = hp->tty;
- kobjp = &hp->kobj;
if (hp->index < MAX_NR_HVC_CONSOLES)
vtermnos[hp->index] = -1;
@@ -821,12 +807,12 @@ int __devexit hvc_remove(struct hvc_struct *hp)
spin_unlock_irqrestore(&hp->lock, flags);
/*
- * We 'put' the instance that was grabbed when the kobject instance
- * was initialized using kobject_init(). Let the last holder of this
- * kobject cause it to be removed, which will probably be the tty_hangup
+ * We 'put' the instance that was grabbed when the kref instance
+ * was initialized using kref_init(). Let the last holder of this
+ * kref cause it to be removed, which will probably be the tty_hangup
* below.
*/
- kobject_put(kobjp);
+ kref_put(&hp->kref, destroy_hvc_struct);
/*
* This function call will auto chain call hvc_hangup. The tty should
diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c
index 69d8866de783..fd7559084b82 100644
--- a/drivers/char/hvcs.c
+++ b/drivers/char/hvcs.c
@@ -57,11 +57,7 @@
* rescanning partner information upon a user's request.
*
* Each vty-server, prior to being exposed to this driver is reference counted
- * using the 2.6 Linux kernel kobject construct. This kobject is also used by
- * the vio bus to provide a vio device sysfs entry that this driver attaches
- * device specific attributes to, including partner information. The vio bus
- * framework also provides a sysfs entry for each vio driver. The hvcs driver
- * provides driver attributes in this entry.
+ * using the 2.6 Linux kernel kref construct.
*
* For direction on installation and usage of this driver please reference
* Documentation/powerpc/hvcs.txt.
@@ -71,7 +67,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
-#include <linux/kobject.h>
+#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/major.h>
@@ -293,12 +289,12 @@ struct hvcs_struct {
int chars_in_buffer;
/*
- * Any variable below the kobject is valid before a tty is connected and
+ * Any variable below the kref is valid before a tty is connected and
* stays valid after the tty is disconnected. These shouldn't be
* whacked until the koject refcount reaches zero though some entries
* may be changed via sysfs initiatives.
*/
- struct kobject kobj; /* ref count & hvcs_struct lifetime */
+ struct kref kref; /* ref count & hvcs_struct lifetime */
int connected; /* is the vty-server currently connected to a vty? */
uint32_t p_unit_address; /* partner unit address */
uint32_t p_partition_ID; /* partner partition ID */
@@ -307,8 +303,8 @@ struct hvcs_struct {
struct vio_dev *vdev;
};
-/* Required to back map a kobject to its containing object */
-#define from_kobj(kobj) container_of(kobj, struct hvcs_struct, kobj)
+/* Required to back map a kref to its containing object */
+#define from_kref(k) container_of(k, struct hvcs_struct, kref)
static struct list_head hvcs_structs = LIST_HEAD_INIT(hvcs_structs);
static DEFINE_SPINLOCK(hvcs_structs_lock);
@@ -334,7 +330,6 @@ static void hvcs_partner_free(struct hvcs_struct *hvcsd);
static int hvcs_enable_device(struct hvcs_struct *hvcsd,
uint32_t unit_address, unsigned int irq, struct vio_dev *dev);
-static void destroy_hvcs_struct(struct kobject *kobj);
static int hvcs_open(struct tty_struct *tty, struct file *filp);
static void hvcs_close(struct tty_struct *tty, struct file *filp);
static void hvcs_hangup(struct tty_struct * tty);
@@ -703,10 +698,10 @@ static void hvcs_return_index(int index)
hvcs_index_list[index] = -1;
}
-/* callback when the kboject ref count reaches zero */
-static void destroy_hvcs_struct(struct kobject *kobj)
+/* callback when the kref ref count reaches zero */
+static void destroy_hvcs_struct(struct kref *kref)
{
- struct hvcs_struct *hvcsd = from_kobj(kobj);
+ struct hvcs_struct *hvcsd = from_kref(kref);
struct vio_dev *vdev;
unsigned long flags;
@@ -743,10 +738,6 @@ static void destroy_hvcs_struct(struct kobject *kobj)
kfree(hvcsd);
}
-static struct kobj_type hvcs_kobj_type = {
- .release = destroy_hvcs_struct,
-};
-
static int hvcs_get_index(void)
{
int i;
@@ -791,9 +782,7 @@ static int __devinit hvcs_probe(
spin_lock_init(&hvcsd->lock);
/* Automatically incs the refcount the first time */
- kobject_init(&hvcsd->kobj);
- /* Set up the callback for terminating the hvcs_struct's life */
- hvcsd->kobj.ktype = &hvcs_kobj_type;
+ kref_init(&hvcsd->kref);
hvcsd->vdev = dev;
dev->dev.driver_data = hvcsd;
@@ -844,7 +833,6 @@ static int __devexit hvcs_remove(struct vio_dev *dev)
{
struct hvcs_struct *hvcsd = dev->dev.driver_data;
unsigned long flags;
- struct kobject *kobjp;
struct tty_struct *tty;
if (!hvcsd)
@@ -856,15 +844,13 @@ static int __devexit hvcs_remove(struct vio_dev *dev)
tty = hvcsd->tty;
- kobjp = &hvcsd->kobj;
-
spin_unlock_irqrestore(&hvcsd->lock, flags);
/*
* Let the last holder of this object cause it to be removed, which
* would probably be tty_hangup below.
*/
- kobject_put (kobjp);
+ kref_put(&hvcsd->kref, destroy_hvcs_struct);
/*
* The hangup is a scheduled function which will auto chain call
@@ -1086,7 +1072,7 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address,
}
/*
- * This always increments the kobject ref count if the call is successful.
+ * This always increments the kref ref count if the call is successful.
* Please remember to dec when you are done with the instance.
*
* NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when
@@ -1103,7 +1089,7 @@ static struct hvcs_struct *hvcs_get_by_index(int index)
list_for_each_entry(hvcsd, &hvcs_structs, next) {
spin_lock_irqsave(&hvcsd->lock, flags);
if (hvcsd->index == index) {
- kobject_get(&hvcsd->kobj);
+ kref_get(&hvcsd->kref);
spin_unlock_irqrestore(&hvcsd->lock, flags);
spin_unlock(&hvcs_structs_lock);
return hvcsd;
@@ -1129,14 +1115,13 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
unsigned int irq;
struct vio_dev *vdev;
unsigned long unit_address;
- struct kobject *kobjp;
if (tty->driver_data)
goto fast_open;
/*
* Is there a vty-server that shares the same index?
- * This function increments the kobject index.
+ * This function increments the kref index.
*/
if (!(hvcsd = hvcs_get_by_index(tty->index))) {
printk(KERN_WARNING "HVCS: open failed, no device associated"
@@ -1181,7 +1166,7 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
* and will grab the spinlock and free the connection if it fails.
*/
if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) {
- kobject_put(&hvcsd->kobj);
+ kref_put(&hvcsd->kref, destroy_hvcs_struct);
printk(KERN_WARNING "HVCS: enable device failed.\n");
return rc;
}
@@ -1192,17 +1177,11 @@ fast_open:
hvcsd = tty->driver_data;
spin_lock_irqsave(&hvcsd->lock, flags);
- if (!kobject_get(&hvcsd->kobj)) {
- spin_unlock_irqrestore(&hvcsd->lock, flags);
- printk(KERN_ERR "HVCS: Kobject of open"
- " hvcs doesn't exist.\n");
- return -EFAULT; /* Is this the right return value? */
- }
-
+ kref_get(&hvcsd->kref);
hvcsd->open_count++;
-
hvcsd->todo_mask |= HVCS_SCHED_READ;
spin_unlock_irqrestore(&hvcsd->lock, flags);
+
open_success:
hvcs_kick();
@@ -1212,9 +1191,8 @@ open_success:
return 0;
error_release:
- kobjp = &hvcsd->kobj;
spin_unlock_irqrestore(&hvcsd->lock, flags);
- kobject_put(&hvcsd->kobj);
+ kref_put(&hvcsd->kref, destroy_hvcs_struct);
printk(KERN_WARNING "HVCS: partner connect failed.\n");
return retval;
@@ -1224,7 +1202,6 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
{
struct hvcs_struct *hvcsd;
unsigned long flags;
- struct kobject *kobjp;
int irq = NO_IRQ;
/*
@@ -1245,7 +1222,6 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
hvcsd = tty->driver_data;
spin_lock_irqsave(&hvcsd->lock, flags);
- kobjp = &hvcsd->kobj;
if (--hvcsd->open_count == 0) {
vio_disable_interrupts(hvcsd->vdev);
@@ -1270,7 +1246,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
tty->driver_data = NULL;
free_irq(irq, hvcsd);
- kobject_put(kobjp);
+ kref_put(&hvcsd->kref, destroy_hvcs_struct);
return;
} else if (hvcsd->open_count < 0) {
printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
@@ -1279,7 +1255,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
}
spin_unlock_irqrestore(&hvcsd->lock, flags);
- kobject_put(kobjp);
+ kref_put(&hvcsd->kref, destroy_hvcs_struct);
}
static void hvcs_hangup(struct tty_struct * tty)
@@ -1287,21 +1263,17 @@ static void hvcs_hangup(struct tty_struct * tty)
struct hvcs_struct *hvcsd = tty->driver_data;
unsigned long flags;
int temp_open_count;
- struct kobject *kobjp;
int irq = NO_IRQ;
spin_lock_irqsave(&hvcsd->lock, flags);
- /* Preserve this so that we know how many kobject refs to put */
+ /* Preserve this so that we know how many kref refs to put */
temp_open_count = hvcsd->open_count;
/*
- * Don't kobject put inside the spinlock because the destruction
+ * Don't kref put inside the spinlock because the destruction
* callback may use the spinlock and it may get called before the
- * spinlock has been released. Get a pointer to the kobject and
- * kobject_put on that after releasing the spinlock.
+ * spinlock has been released.
*/
- kobjp = &hvcsd->kobj;
-
vio_disable_interrupts(hvcsd->vdev);
hvcsd->todo_mask = 0;
@@ -1324,7 +1296,7 @@ static void hvcs_hangup(struct tty_struct * tty)
free_irq(irq, hvcsd);
/*
- * We need to kobject_put() for every open_count we have since the
+ * We need to kref_put() for every open_count we have since the
* tty_hangup() function doesn't invoke a close per open connection on a
* non-console device.
*/
@@ -1335,7 +1307,7 @@ static void hvcs_hangup(struct tty_struct * tty)
* NOTE: If this hangup was signaled from user space then the
* final put will never happen.
*/
- kobject_put(kobjp);
+ kref_put(&hvcsd->kref, destroy_hvcs_struct);
}
}
diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c
index 556fd81fa815..c422e870dc52 100644
--- a/drivers/char/hw_random/amd-rng.c
+++ b/drivers/char/hw_random/amd-rng.c
@@ -28,6 +28,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/io.h>
@@ -52,11 +53,18 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct pci_dev *amd_pdev;
-static int amd_rng_data_present(struct hwrng *rng)
+static int amd_rng_data_present(struct hwrng *rng, int wait)
{
u32 pmbase = (u32)rng->priv;
+ int data, i;
- return !!(inl(pmbase + 0xF4) & 1);
+ for (i = 0; i < 20; i++) {
+ data = !!(inl(pmbase + 0xF4) & 1);
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}
static int amd_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 26a860adcb38..0118b9817a95 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -66,11 +66,11 @@ static inline void hwrng_cleanup(struct hwrng *rng)
rng->cleanup(rng);
}
-static inline int hwrng_data_present(struct hwrng *rng)
+static inline int hwrng_data_present(struct hwrng *rng, int wait)
{
if (!rng->data_present)
return 1;
- return rng->data_present(rng);
+ return rng->data_present(rng, wait);
}
static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
@@ -94,8 +94,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
{
u32 data;
ssize_t ret = 0;
- int i, err = 0;
- int data_present;
+ int err = 0;
int bytes_read;
while (size) {
@@ -107,21 +106,10 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
err = -ENODEV;
goto out;
}
- if (filp->f_flags & O_NONBLOCK) {
- data_present = hwrng_data_present(current_rng);
- } else {
- /* Some RNG require some time between data_reads to gather
- * new entropy. Poll it.
- */
- for (i = 0; i < 20; i++) {
- data_present = hwrng_data_present(current_rng);
- if (data_present)
- break;
- udelay(10);
- }
- }
+
bytes_read = 0;
- if (data_present)
+ if (hwrng_data_present(current_rng,
+ !(filp->f_flags & O_NONBLOCK)))
bytes_read = hwrng_data_read(current_rng, &data);
mutex_unlock(&rng_mutex);
diff --git a/drivers/char/hw_random/geode-rng.c b/drivers/char/hw_random/geode-rng.c
index 8e8658dcd2e3..fed4ef5569f5 100644
--- a/drivers/char/hw_random/geode-rng.c
+++ b/drivers/char/hw_random/geode-rng.c
@@ -28,6 +28,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/io.h>
@@ -61,11 +62,18 @@ static int geode_rng_data_read(struct hwrng *rng, u32 *data)
return 4;
}
-static int geode_rng_data_present(struct hwrng *rng)
+static int geode_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *mem = (void __iomem *)rng->priv;
+ int data, i;
- return !!(readl(mem + GEODE_RNG_STATUS_REG));
+ for (i = 0; i < 20; i++) {
+ data = !!(readl(mem + GEODE_RNG_STATUS_REG));
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}
diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c
index 753f46052b87..5cc651ef75eb 100644
--- a/drivers/char/hw_random/intel-rng.c
+++ b/drivers/char/hw_random/intel-rng.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/stop_machine.h>
+#include <linux/delay.h>
#include <asm/io.h>
@@ -162,11 +163,19 @@ static inline u8 hwstatus_set(void __iomem *mem,
return hwstatus_get(mem);
}
-static int intel_rng_data_present(struct hwrng *rng)
+static int intel_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *mem = (void __iomem *)rng->priv;
-
- return !!(readb(mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT);
+ int data, i;
+
+ for (i = 0; i < 20; i++) {
+ data = !!(readb(mem + INTEL_RNG_STATUS) &
+ INTEL_RNG_DATA_PRESENT);
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}
static int intel_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 3f35a1c562b1..7e319951fa41 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -29,6 +29,7 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/io.h>
@@ -65,9 +66,17 @@ static void omap_rng_write_reg(int reg, u32 val)
}
/* REVISIT: Does the status bit really work on 16xx? */
-static int omap_rng_data_present(struct hwrng *rng)
+static int omap_rng_data_present(struct hwrng *rng, int wait)
{
- return omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
+ int data, i;
+
+ for (i = 0; i < 20; i++) {
+ data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}
static int omap_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c
index fa6040b6c8f2..e2ea210cfa5f 100644
--- a/drivers/char/hw_random/pasemi-rng.c
+++ b/drivers/char/hw_random/pasemi-rng.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/of_platform.h>
#include <asm/io.h>
@@ -41,12 +42,19 @@
#define MODULE_NAME "pasemi_rng"
-static int pasemi_rng_data_present(struct hwrng *rng)
+static int pasemi_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *rng_regs = (void __iomem *)rng->priv;
-
- return (in_le32(rng_regs + SDCRNG_CTL_REG)
- & SDCRNG_CTL_FVLD_M) ? 1 : 0;
+ int data, i;
+
+ for (i = 0; i < 20; i++) {
+ data = (in_le32(rng_regs + SDCRNG_CTL_REG)
+ & SDCRNG_CTL_FVLD_M) ? 1 : 0;
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}
static int pasemi_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index ec435cb25c4f..868e39fd42e4 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/cpufeature.h>
@@ -77,10 +78,11 @@ static inline u32 xstore(u32 *addr, u32 edx_in)
return eax_out;
}
-static int via_rng_data_present(struct hwrng *rng)
+static int via_rng_data_present(struct hwrng *rng, int wait)
{
u32 bytes_out;
u32 *via_rng_datum = (u32 *)(&rng->priv);
+ int i;
/* We choose the recommended 1-byte-per-instruction RNG rate,
* for greater randomness at the expense of speed. Larger
@@ -95,12 +97,15 @@ static int via_rng_data_present(struct hwrng *rng)
* completes.
*/
- *via_rng_datum = 0; /* paranoia, not really necessary */
- bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1);
- bytes_out &= VIA_XSTORE_CNT_MASK;
- if (bytes_out == 0)
- return 0;
- return 1;
+ for (i = 0; i < 20; i++) {
+ *via_rng_datum = 0; /* paranoia, not really necessary */
+ bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1);
+ bytes_out &= VIA_XSTORE_CNT_MASK;
+ if (bytes_out || !wait)
+ break;
+ udelay(10);
+ }
+ return bytes_out ? 1 : 0;
}
static int via_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c
new file mode 100644
index 000000000000..6076e662886a
--- /dev/null
+++ b/drivers/char/nozomi.c
@@ -0,0 +1,1993 @@
+/*
+ * nozomi.c -- HSDPA driver Broadband Wireless Data Card - Globe Trotter
+ *
+ * Written by: Ulf Jakobsson,
+ * Jan �erfeldt,
+ * Stefan Thomasson,
+ *
+ * Maintained by: Paul Hardwick (p.hardwick@option.com)
+ *
+ * Patches:
+ * Locking code changes for Vodafone by Sphere Systems Ltd,
+ * Andrew Bird (ajb@spheresystems.co.uk )
+ * & Phil Sanderson
+ *
+ * Source has been ported from an implementation made by Filip Aben @ Option
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Copyright (c) 2005,2006 Option Wireless Sweden AB
+ * Copyright (c) 2006 Sphere Systems Ltd
+ * Copyright (c) 2006 Option Wireless n/v
+ * All rights Reserved.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * --------------------------------------------------------------------------
+ */
+
+/*
+ * CHANGELOG
+ * Version 2.1d
+ * 11-November-2007 Jiri Slaby, Frank Seidel
+ * - Big rework of multicard support by Jiri
+ * - Major cleanups (semaphore to mutex, endianess, no major reservation)
+ * - Optimizations
+ *
+ * Version 2.1c
+ * 30-October-2007 Frank Seidel
+ * - Completed multicard support
+ * - Minor cleanups
+ *
+ * Version 2.1b
+ * 07-August-2007 Frank Seidel
+ * - Minor cleanups
+ * - theoretical multicard support
+ *
+ * Version 2.1
+ * 03-July-2006 Paul Hardwick
+ *
+ * - Stability Improvements. Incorporated spinlock wraps patch.
+ * - Updated for newer 2.6.14+ kernels (tty_buffer_request_room)
+ * - using __devexit macro for tty
+ *
+ *
+ * Version 2.0
+ * 08-feb-2006 15:34:10:Ulf
+ *
+ * -Fixed issue when not waking up line disipine layer, could probably result
+ * in better uplink performance for 2.4.
+ *
+ * -Fixed issue with big endian during initalization, now proper toggle flags
+ * are handled between preloader and maincode.
+ *
+ * -Fixed flow control issue.
+ *
+ * -Added support for setting DTR.
+ *
+ * -For 2.4 kernels, removing temporary buffer that's not needed.
+ *
+ * -Reading CTS only for modem port (only port that supports it).
+ *
+ * -Return 0 in write_room instead of netative value, it's not handled in
+ * upper layer.
+ *
+ * --------------------------------------------------------------------------
+ * Version 1.0
+ *
+ * First version of driver, only tested with card of type F32_2.
+ * Works fine with 2.4 and 2.6 kernels.
+ * Driver also support big endian architecture.
+ */
+
+/* Enable this to have a lot of debug printouts */
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/kfifo.h>
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+
+#include <linux/delay.h>
+
+
+#define VERSION_STRING DRIVER_DESC " 2.1d (build date: " \
+ __DATE__ " " __TIME__ ")"
+
+/* Macros definitions */
+
+/* Default debug printout level */
+#define NOZOMI_DEBUG_LEVEL 0x00
+
+#define P_BUF_SIZE 128
+#define NFO(_err_flag_, args...) \
+do { \
+ char tmp[P_BUF_SIZE]; \
+ snprintf(tmp, sizeof(tmp), ##args); \
+ printk(_err_flag_ "[%d] %s(): %s\n", __LINE__, \
+ __FUNCTION__, tmp); \
+} while (0)
+
+#define DBG1(args...) D_(0x01, ##args)
+#define DBG2(args...) D_(0x02, ##args)
+#define DBG3(args...) D_(0x04, ##args)
+#define DBG4(args...) D_(0x08, ##args)
+#define DBG5(args...) D_(0x10, ##args)
+#define DBG6(args...) D_(0x20, ##args)
+#define DBG7(args...) D_(0x40, ##args)
+#define DBG8(args...) D_(0x80, ##args)
+
+#ifdef DEBUG
+/* Do we need this settable at runtime? */
+static int debug = NOZOMI_DEBUG_LEVEL;
+
+#define D(lvl, args...) do {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \
+ while (0)
+#define D_(lvl, args...) D(lvl, ##args)
+
+/* These printouts are always printed */
+
+#else
+static int debug;
+#define D_(lvl, args...)
+#endif
+
+/* TODO: rewrite to optimize macros... */
+
+#define TMP_BUF_MAX 256
+
+#define DUMP(buf__,len__) \
+ do { \
+ char tbuf[TMP_BUF_MAX] = {0};\
+ if (len__ > 1) {\
+ snprintf(tbuf, len__ > TMP_BUF_MAX ? TMP_BUF_MAX : len__, "%s", buf__);\
+ if (tbuf[len__-2] == '\r') {\
+ tbuf[len__-2] = 'r';\
+ } \
+ DBG1("SENDING: '%s' (%d+n)", tbuf, len__);\
+ } else {\
+ DBG1("SENDING: '%s' (%d)", tbuf, len__);\
+ } \
+} while (0)
+
+/* Defines */
+#define NOZOMI_NAME "nozomi"
+#define NOZOMI_NAME_TTY "nozomi_tty"
+#define DRIVER_DESC "Nozomi driver"
+
+#define NTTY_TTY_MAXMINORS 256
+#define NTTY_FIFO_BUFFER_SIZE 8192
+
+/* Must be power of 2 */
+#define FIFO_BUFFER_SIZE_UL 8192
+
+/* Size of tmp send buffer to card */
+#define SEND_BUF_MAX 1024
+#define RECEIVE_BUF_MAX 4
+
+
+/* Define all types of vendors and devices to support */
+#define VENDOR1 0x1931 /* Vendor Option */
+#define DEVICE1 0x000c /* HSDPA card */
+
+#define R_IIR 0x0000 /* Interrupt Identity Register */
+#define R_FCR 0x0000 /* Flow Control Register */
+#define R_IER 0x0004 /* Interrupt Enable Register */
+
+#define CONFIG_MAGIC 0xEFEFFEFE
+#define TOGGLE_VALID 0x0000
+
+/* Definition of interrupt tokens */
+#define MDM_DL1 0x0001
+#define MDM_UL1 0x0002
+#define MDM_DL2 0x0004
+#define MDM_UL2 0x0008
+#define DIAG_DL1 0x0010
+#define DIAG_DL2 0x0020
+#define DIAG_UL 0x0040
+#define APP1_DL 0x0080
+#define APP1_UL 0x0100
+#define APP2_DL 0x0200
+#define APP2_UL 0x0400
+#define CTRL_DL 0x0800
+#define CTRL_UL 0x1000
+#define RESET 0x8000
+
+#define MDM_DL (MDM_DL1 | MDM_DL2)
+#define MDM_UL (MDM_UL1 | MDM_UL2)
+#define DIAG_DL (DIAG_DL1 | DIAG_DL2)
+
+/* modem signal definition */
+#define CTRL_DSR 0x0001
+#define CTRL_DCD 0x0002
+#define CTRL_RI 0x0004
+#define CTRL_CTS 0x0008
+
+#define CTRL_DTR 0x0001
+#define CTRL_RTS 0x0002
+
+#define MAX_PORT 4
+#define NOZOMI_MAX_PORTS 5
+#define NOZOMI_MAX_CARDS (NTTY_TTY_MAXMINORS / MAX_PORT)
+
+/* Type definitions */
+
+/*
+ * There are two types of nozomi cards,
+ * one with 2048 memory and with 8192 memory
+ */
+enum card_type {
+ F32_2 = 2048, /* 512 bytes downlink + uplink * 2 -> 2048 */
+ F32_8 = 8192, /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */
+};
+
+/* Two different toggle channels exist */
+enum channel_type {
+ CH_A = 0,
+ CH_B = 1,
+};
+
+/* Port definition for the card regarding flow control */
+enum ctrl_port_type {
+ CTRL_CMD = 0,
+ CTRL_MDM = 1,
+ CTRL_DIAG = 2,
+ CTRL_APP1 = 3,
+ CTRL_APP2 = 4,
+ CTRL_ERROR = -1,
+};
+
+/* Ports that the nozomi has */
+enum port_type {
+ PORT_MDM = 0,
+ PORT_DIAG = 1,
+ PORT_APP1 = 2,
+ PORT_APP2 = 3,
+ PORT_CTRL = 4,
+ PORT_ERROR = -1,
+};
+
+#ifdef __BIG_ENDIAN
+/* Big endian */
+
+struct toggles {
+ unsigned enabled:5; /*
+ * Toggle fields are valid if enabled is 0,
+ * else A-channels must always be used.
+ */
+ unsigned diag_dl:1;
+ unsigned mdm_dl:1;
+ unsigned mdm_ul:1;
+} __attribute__ ((packed));
+
+/* Configuration table to read at startup of card */
+/* Is for now only needed during initialization phase */
+struct config_table {
+ u32 signature;
+ u16 product_information;
+ u16 version;
+ u8 pad3[3];
+ struct toggles toggle;
+ u8 pad1[4];
+ u16 dl_mdm_len1; /*
+ * If this is 64, it can hold
+ * 60 bytes + 4 that is length field
+ */
+ u16 dl_start;
+
+ u16 dl_diag_len1;
+ u16 dl_mdm_len2; /*
+ * If this is 64, it can hold
+ * 60 bytes + 4 that is length field
+ */
+ u16 dl_app1_len;
+
+ u16 dl_diag_len2;
+ u16 dl_ctrl_len;
+ u16 dl_app2_len;
+ u8 pad2[16];
+ u16 ul_mdm_len1;
+ u16 ul_start;
+ u16 ul_diag_len;
+ u16 ul_mdm_len2;
+ u16 ul_app1_len;
+ u16 ul_app2_len;
+ u16 ul_ctrl_len;
+} __attribute__ ((packed));
+
+/* This stores all control downlink flags */
+struct ctrl_dl {
+ u8 port;
+ unsigned reserved:4;
+ unsigned CTS:1;
+ unsigned RI:1;
+ unsigned DCD:1;
+ unsigned DSR:1;
+} __attribute__ ((packed));
+
+/* This stores all control uplink flags */
+struct ctrl_ul {
+ u8 port;
+ unsigned reserved:6;
+ unsigned RTS:1;
+ unsigned DTR:1;
+} __attribute__ ((packed));
+
+#else
+/* Little endian */
+
+/* This represents the toggle information */
+struct toggles {
+ unsigned mdm_ul:1;
+ unsigned mdm_dl:1;
+ unsigned diag_dl:1;
+ unsigned enabled:5; /*
+ * Toggle fields are valid if enabled is 0,
+ * else A-channels must always be used.
+ */
+} __attribute__ ((packed));
+
+/* Configuration table to read at startup of card */
+struct config_table {
+ u32 signature;
+ u16 version;
+ u16 product_information;
+ struct toggles toggle;
+ u8 pad1[7];
+ u16 dl_start;
+ u16 dl_mdm_len1; /*
+ * If this is 64, it can hold
+ * 60 bytes + 4 that is length field
+ */
+ u16 dl_mdm_len2;
+ u16 dl_diag_len1;
+ u16 dl_diag_len2;
+ u16 dl_app1_len;
+ u16 dl_app2_len;
+ u16 dl_ctrl_len;
+ u8 pad2[16];
+ u16 ul_start;
+ u16 ul_mdm_len2;
+ u16 ul_mdm_len1;
+ u16 ul_diag_len;
+ u16 ul_app1_len;
+ u16 ul_app2_len;
+ u16 ul_ctrl_len;
+} __attribute__ ((packed));
+
+/* This stores all control downlink flags */
+struct ctrl_dl {
+ unsigned DSR:1;
+ unsigned DCD:1;
+ unsigned RI:1;
+ unsigned CTS:1;
+ unsigned reserverd:4;
+ u8 port;
+} __attribute__ ((packed));
+
+/* This stores all control uplink flags */
+struct ctrl_ul {
+ unsigned DTR:1;
+ unsigned RTS:1;
+ unsigned reserved:6;
+ u8 port;
+} __attribute__ ((packed));
+#endif
+
+/* This holds all information that is needed regarding a port */
+struct port {
+ u8 update_flow_control;
+ struct ctrl_ul ctrl_ul;
+ struct ctrl_dl ctrl_dl;
+ struct kfifo *fifo_ul;
+ void __iomem *dl_addr[2];
+ u32 dl_size[2];
+ u8 toggle_dl;
+ void __iomem *ul_addr[2];
+ u32 ul_size[2];
+ u8 toggle_ul;
+ u16 token_dl;
+
+ struct tty_struct *tty;
+ int tty_open_count;
+ /* mutex to ensure one access patch to this port */
+ struct mutex tty_sem;
+ wait_queue_head_t tty_wait;
+ struct async_icount tty_icount;
+};
+
+/* Private data one for each card in the system */
+struct nozomi {
+ void __iomem *base_addr;
+ unsigned long flip;
+
+ /* Pointers to registers */
+ void __iomem *reg_iir;
+ void __iomem *reg_fcr;
+ void __iomem *reg_ier;
+
+ u16 last_ier;
+ enum card_type card_type;
+ struct config_table config_table; /* Configuration table */
+ struct pci_dev *pdev;
+ struct port port[NOZOMI_MAX_PORTS];
+ u8 *send_buf;
+
+ spinlock_t spin_mutex; /* secures access to registers and tty */
+
+ unsigned int index_start;
+ u32 open_ttys;
+};
+
+/* This is a data packet that is read or written to/from card */
+struct buffer {
+ u32 size; /* size is the length of the data buffer */
+ u8 *data;
+} __attribute__ ((packed));
+
+/* Global variables */
+static struct pci_device_id nozomi_pci_tbl[] = {
+ {PCI_DEVICE(VENDOR1, DEVICE1)},
+ {},
+};
+
+MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl);
+
+static struct nozomi *ndevs[NOZOMI_MAX_CARDS];
+static struct tty_driver *ntty_driver;
+
+/*
+ * find card by tty_index
+ */
+static inline struct nozomi *get_dc_by_tty(const struct tty_struct *tty)
+{
+ return tty ? ndevs[tty->index / MAX_PORT] : NULL;
+}
+
+static inline struct port *get_port_by_tty(const struct tty_struct *tty)
+{
+ struct nozomi *ndev = get_dc_by_tty(tty);
+ return ndev ? &ndev->port[tty->index % MAX_PORT] : NULL;
+}
+
+/*
+ * TODO:
+ * -Optimize
+ * -Rewrite cleaner
+ */
+
+static void read_mem32(u32 *buf, const void __iomem *mem_addr_start,
+ u32 size_bytes)
+{
+ u32 i = 0;
+ const u32 *ptr = (__force u32 *) mem_addr_start;
+ u16 *buf16;
+
+ if (unlikely(!ptr || !buf))
+ goto out;
+
+ /* shortcut for extremely often used cases */
+ switch (size_bytes) {
+ case 2: /* 2 bytes */
+ buf16 = (u16 *) buf;
+ *buf16 = __le16_to_cpu(readw((void __iomem *)ptr));
+ goto out;
+ break;
+ case 4: /* 4 bytes */
+ *(buf) = __le32_to_cpu(readl((void __iomem *)ptr));
+ goto out;
+ break;
+ }
+
+ while (i < size_bytes) {
+ if (size_bytes - i == 2) {
+ /* Handle 2 bytes in the end */
+ buf16 = (u16 *) buf;
+ *(buf16) = __le16_to_cpu(readw((void __iomem *)ptr));
+ i += 2;
+ } else {
+ /* Read 4 bytes */
+ *(buf) = __le32_to_cpu(readl((void __iomem *)ptr));
+ i += 4;
+ }
+ buf++;
+ ptr++;
+ }
+out:
+ return;
+}
+
+/*
+ * TODO:
+ * -Optimize
+ * -Rewrite cleaner
+ */
+static u32 write_mem32(void __iomem *mem_addr_start, u32 *buf,
+ u32 size_bytes)
+{
+ u32 i = 0;
+ u32 *ptr = (__force u32 *) mem_addr_start;
+ u16 *buf16;
+
+ if (unlikely(!ptr || !buf))
+ return 0;
+
+ /* shortcut for extremely often used cases */
+ switch (size_bytes) {
+ case 2: /* 2 bytes */
+ buf16 = (u16 *) buf;
+ writew(__cpu_to_le16(*buf16), (void __iomem *)ptr);
+ return 2;
+ break;
+ case 1: /*
+ * also needs to write 4 bytes in this case
+ * so falling through..
+ */
+ case 4: /* 4 bytes */
+ writel(__cpu_to_le32(*buf), (void __iomem *)ptr);
+ return 4;
+ break;
+ }
+
+ while (i < size_bytes) {
+ if (size_bytes - i == 2) {
+ /* 2 bytes */
+ buf16 = (u16 *) buf;
+ writew(__cpu_to_le16(*buf16), (void __iomem *)ptr);
+ i += 2;
+ } else {
+ /* 4 bytes */
+ writel(__cpu_to_le32(*buf), (void __iomem *)ptr);
+ i += 4;
+ }
+ buf++;
+ ptr++;
+ }
+ return i;
+}
+
+/* Setup pointers to different channels and also setup buffer sizes. */
+static void setup_memory(struct nozomi *dc)
+{
+ void __iomem *offset = dc->base_addr + dc->config_table.dl_start;
+ /* The length reported is including the length field of 4 bytes,
+ * hence subtract with 4.
+ */
+ const u16 buff_offset = 4;
+
+ /* Modem port dl configuration */
+ dc->port[PORT_MDM].dl_addr[CH_A] = offset;
+ dc->port[PORT_MDM].dl_addr[CH_B] =
+ (offset += dc->config_table.dl_mdm_len1);
+ dc->port[PORT_MDM].dl_size[CH_A] =
+ dc->config_table.dl_mdm_len1 - buff_offset;
+ dc->port[PORT_MDM].dl_size[CH_B] =
+ dc->config_table.dl_mdm_len2 - buff_offset;
+
+ /* Diag port dl configuration */
+ dc->port[PORT_DIAG].dl_addr[CH_A] =
+ (offset += dc->config_table.dl_mdm_len2);
+ dc->port[PORT_DIAG].dl_size[CH_A] =
+ dc->config_table.dl_diag_len1 - buff_offset;
+ dc->port[PORT_DIAG].dl_addr[CH_B] =
+ (offset += dc->config_table.dl_diag_len1);
+ dc->port[PORT_DIAG].dl_size[CH_B] =
+ dc->config_table.dl_diag_len2 - buff_offset;
+
+ /* App1 port dl configuration */
+ dc->port[PORT_APP1].dl_addr[CH_A] =
+ (offset += dc->config_table.dl_diag_len2);
+ dc->port[PORT_APP1].dl_size[CH_A] =
+ dc->config_table.dl_app1_len - buff_offset;
+
+ /* App2 port dl configuration */
+ dc->port[PORT_APP2].dl_addr[CH_A] =
+ (offset += dc->config_table.dl_app1_len);
+ dc->port[PORT_APP2].dl_size[CH_A] =
+ dc->config_table.dl_app2_len - buff_offset;
+
+ /* Ctrl dl configuration */
+ dc->port[PORT_CTRL].dl_addr[CH_A] =
+ (offset += dc->config_table.dl_app2_len);
+ dc->port[PORT_CTRL].dl_size[CH_A] =
+ dc->config_table.dl_ctrl_len - buff_offset;
+
+ offset = dc->base_addr + dc->config_table.ul_start;
+
+ /* Modem Port ul configuration */
+ dc->port[PORT_MDM].ul_addr[CH_A] = offset;
+ dc->port[PORT_MDM].ul_size[CH_A] =
+ dc->config_table.ul_mdm_len1 - buff_offset;
+ dc->port[PORT_MDM].ul_addr[CH_B] =
+ (offset += dc->config_table.ul_mdm_len1);
+ dc->port[PORT_MDM].ul_size[CH_B] =
+ dc->config_table.ul_mdm_len2 - buff_offset;
+
+ /* Diag port ul configuration */
+ dc->port[PORT_DIAG].ul_addr[CH_A] =
+ (offset += dc->config_table.ul_mdm_len2);
+ dc->port[PORT_DIAG].ul_size[CH_A] =
+ dc->config_table.ul_diag_len - buff_offset;
+
+ /* App1 port ul configuration */
+ dc->port[PORT_APP1].ul_addr[CH_A] =
+ (offset += dc->config_table.ul_diag_len);
+ dc->port[PORT_APP1].ul_size[CH_A] =
+ dc->config_table.ul_app1_len - buff_offset;
+
+ /* App2 port ul configuration */
+ dc->port[PORT_APP2].ul_addr[CH_A] =
+ (offset += dc->config_table.ul_app1_len);
+ dc->port[PORT_APP2].ul_size[CH_A] =
+ dc->config_table.ul_app2_len - buff_offset;
+
+ /* Ctrl ul configuration */
+ dc->port[PORT_CTRL].ul_addr[CH_A] =
+ (offset += dc->config_table.ul_app2_len);
+ dc->port[PORT_CTRL].ul_size[CH_A] =
+ dc->config_table.ul_ctrl_len - buff_offset;
+}
+
+/* Dump config table under initalization phase */
+#ifdef DEBUG
+static void dump_table(const struct nozomi *dc)
+{
+ DBG3("signature: 0x%08X", dc->config_table.signature);
+ DBG3("version: 0x%04X", dc->config_table.version);
+ DBG3("product_information: 0x%04X", \
+ dc->config_table.product_information);
+ DBG3("toggle enabled: %d", dc->config_table.toggle.enabled);
+ DBG3("toggle up_mdm: %d", dc->config_table.toggle.mdm_ul);
+ DBG3("toggle dl_mdm: %d", dc->config_table.toggle.mdm_dl);
+ DBG3("toggle dl_dbg: %d", dc->config_table.toggle.diag_dl);
+
+ DBG3("dl_start: 0x%04X", dc->config_table.dl_start);
+ DBG3("dl_mdm_len0: 0x%04X, %d", dc->config_table.dl_mdm_len1,
+ dc->config_table.dl_mdm_len1);
+ DBG3("dl_mdm_len1: 0x%04X, %d", dc->config_table.dl_mdm_len2,
+ dc->config_table.dl_mdm_len2);
+ DBG3("dl_diag_len0: 0x%04X, %d", dc->config_table.dl_diag_len1,
+ dc->config_table.dl_diag_len1);
+ DBG3("dl_diag_len1: 0x%04X, %d", dc->config_table.dl_diag_len2,
+ dc->config_table.dl_diag_len2);
+ DBG3("dl_app1_len: 0x%04X, %d", dc->config_table.dl_app1_len,
+ dc->config_table.dl_app1_len);
+ DBG3("dl_app2_len: 0x%04X, %d", dc->config_table.dl_app2_len,
+ dc->config_table.dl_app2_len);
+ DBG3("dl_ctrl_len: 0x%04X, %d", dc->config_table.dl_ctrl_len,
+ dc->config_table.dl_ctrl_len);
+ DBG3("ul_start: 0x%04X, %d", dc->config_table.ul_start,
+ dc->config_table.ul_start);
+ DBG3("ul_mdm_len[0]: 0x%04X, %d", dc->config_table.ul_mdm_len1,
+ dc->config_table.ul_mdm_len1);
+ DBG3("ul_mdm_len[1]: 0x%04X, %d", dc->config_table.ul_mdm_len2,
+ dc->config_table.ul_mdm_len2);
+ DBG3("ul_diag_len: 0x%04X, %d", dc->config_table.ul_diag_len,
+ dc->config_table.ul_diag_len);
+ DBG3("ul_app1_len: 0x%04X, %d", dc->config_table.ul_app1_len,
+ dc->config_table.ul_app1_len);
+ DBG3("ul_app2_len: 0x%04X, %d", dc->config_table.ul_app2_len,
+ dc->config_table.ul_app2_len);
+ DBG3("ul_ctrl_len: 0x%04X, %d", dc->config_table.ul_ctrl_len,
+ dc->config_table.ul_ctrl_len);
+}
+#else
+static __inline__ void dump_table(const struct nozomi *dc) { }
+#endif
+
+/*
+ * Read configuration table from card under intalization phase
+ * Returns 1 if ok, else 0
+ */
+static int nozomi_read_config_table(struct nozomi *dc)
+{
+ read_mem32((u32 *) &dc->config_table, dc->base_addr + 0,
+ sizeof(struct config_table));
+
+ if (dc->config_table.signature != CONFIG_MAGIC) {
+ dev_err(&dc->pdev->dev, "ConfigTable Bad! 0x%08X != 0x%08X\n",
+ dc->config_table.signature, CONFIG_MAGIC);
+ return 0;
+ }
+
+ if ((dc->config_table.version == 0)
+ || (dc->config_table.toggle.enabled == TOGGLE_VALID)) {
+ int i;
+ DBG1("Second phase, configuring card");
+
+ setup_memory(dc);
+
+ dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul;
+ dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl;
+ dc->port[PORT_DIAG].toggle_dl = dc->config_table.toggle.diag_dl;
+ DBG1("toggle ports: MDM UL:%d MDM DL:%d, DIAG DL:%d",
+ dc->port[PORT_MDM].toggle_ul,
+ dc->port[PORT_MDM].toggle_dl, dc->port[PORT_DIAG].toggle_dl);
+
+ dump_table(dc);
+
+ for (i = PORT_MDM; i < MAX_PORT; i++) {
+ dc->port[i].fifo_ul =
+ kfifo_alloc(FIFO_BUFFER_SIZE_UL, GFP_ATOMIC, NULL);
+ memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl));
+ memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul));
+ }
+
+ /* Enable control channel */
+ dc->last_ier = dc->last_ier | CTRL_DL;
+ writew(dc->last_ier, dc->reg_ier);
+
+ dev_info(&dc->pdev->dev, "Initialization OK!\n");
+ return 1;
+ }
+
+ if ((dc->config_table.version > 0)
+ && (dc->config_table.toggle.enabled != TOGGLE_VALID)) {
+ u32 offset = 0;
+ DBG1("First phase: pushing upload buffers, clearing download");
+
+ dev_info(&dc->pdev->dev, "Version of card: %d\n",
+ dc->config_table.version);
+
+ /* Here we should disable all I/O over F32. */
+ setup_memory(dc);
+
+ /*
+ * We should send ALL channel pair tokens back along
+ * with reset token
+ */
+
+ /* push upload modem buffers */
+ write_mem32(dc->port[PORT_MDM].ul_addr[CH_A],
+ (u32 *) &offset, 4);
+ write_mem32(dc->port[PORT_MDM].ul_addr[CH_B],
+ (u32 *) &offset, 4);
+
+ writew(MDM_UL | DIAG_DL | MDM_DL, dc->reg_fcr);
+
+ DBG1("First phase done");
+ }
+
+ return 1;
+}
+
+/* Enable uplink interrupts */
+static void enable_transmit_ul(enum port_type port, struct nozomi *dc)
+{
+ u16 mask[NOZOMI_MAX_PORTS] = \
+ {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL};
+
+ if (port < NOZOMI_MAX_PORTS) {
+ dc->last_ier |= mask[port];
+ writew(dc->last_ier, dc->reg_ier);
+ } else {
+ dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+ }
+}
+
+/* Disable uplink interrupts */
+static void disable_transmit_ul(enum port_type port, struct nozomi *dc)
+{
+ u16 mask[NOZOMI_MAX_PORTS] = \
+ {~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL};
+
+ if (port < NOZOMI_MAX_PORTS) {
+ dc->last_ier &= mask[port];
+ writew(dc->last_ier, dc->reg_ier);
+ } else {
+ dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+ }
+}
+
+/* Enable downlink interrupts */
+static void enable_transmit_dl(enum port_type port, struct nozomi *dc)
+{
+ u16 mask[NOZOMI_MAX_PORTS] = \
+ {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL};
+
+ if (port < NOZOMI_MAX_PORTS) {
+ dc->last_ier |= mask[port];
+ writew(dc->last_ier, dc->reg_ier);
+ } else {
+ dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+ }
+}
+
+/* Disable downlink interrupts */
+static void disable_transmit_dl(enum port_type port, struct nozomi *dc)
+{
+ u16 mask[NOZOMI_MAX_PORTS] = \
+ {~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL};
+
+ if (port < NOZOMI_MAX_PORTS) {
+ dc->last_ier &= mask[port];
+ writew(dc->last_ier, dc->reg_ier);
+ } else {
+ dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+ }
+}
+
+/*
+ * Return 1 - send buffer to card and ack.
+ * Return 0 - don't ack, don't send buffer to card.
+ */
+static int send_data(enum port_type index, struct nozomi *dc)
+{
+ u32 size = 0;
+ struct port *port = &dc->port[index];
+ u8 toggle = port->toggle_ul;
+ void __iomem *addr = port->ul_addr[toggle];
+ u32 ul_size = port->ul_size[toggle];
+ struct tty_struct *tty = port->tty;
+
+ /* Get data from tty and place in buf for now */
+ size = __kfifo_get(port->fifo_ul, dc->send_buf,
+ ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX);
+
+ if (size == 0) {
+ DBG4("No more data to send, disable link:");
+ return 0;
+ }
+
+ /* DUMP(buf, size); */
+
+ /* Write length + data */
+ write_mem32(addr, (u32 *) &size, 4);
+ write_mem32(addr + 4, (u32 *) dc->send_buf, size);
+
+ if (tty)
+ tty_wakeup(tty);
+
+ return 1;
+}
+
+/* If all data has been read, return 1, else 0 */
+static int receive_data(enum port_type index, struct nozomi *dc)
+{
+ u8 buf[RECEIVE_BUF_MAX] = { 0 };
+ int size;
+ u32 offset = 4;
+ struct port *port = &dc->port[index];
+ void __iomem *addr = port->dl_addr[port->toggle_dl];
+ struct tty_struct *tty = port->tty;
+ int i;
+
+ if (unlikely(!tty)) {
+ DBG1("tty not open for port: %d?", index);
+ return 1;
+ }
+
+ read_mem32((u32 *) &size, addr, 4);
+ /* DBG1( "%d bytes port: %d", size, index); */
+
+ if (test_bit(TTY_THROTTLED, &tty->flags)) {
+ DBG1("No room in tty, don't read data, don't ack interrupt, "
+ "disable interrupt");
+
+ /* disable interrupt in downlink... */
+ disable_transmit_dl(index, dc);
+ return 0;
+ }
+
+ if (unlikely(size == 0)) {
+ dev_err(&dc->pdev->dev, "size == 0?\n");
+ return 1;
+ }
+
+ tty_buffer_request_room(tty, size);
+
+ while (size > 0) {
+ read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX);
+
+ if (size == 1) {
+ tty_insert_flip_char(tty, buf[0], TTY_NORMAL);
+ size = 0;
+ } else if (size < RECEIVE_BUF_MAX) {
+ size -= tty_insert_flip_string(tty, (char *) buf, size);
+ } else {
+ i = tty_insert_flip_string(tty, \
+ (char *) buf, RECEIVE_BUF_MAX);
+ size -= i;
+ offset += i;
+ }
+ }
+
+ set_bit(index, &dc->flip);
+
+ return 1;
+}
+
+/* Debug for interrupts */
+#ifdef DEBUG
+static char *interrupt2str(u16 interrupt)
+{
+ static char buf[TMP_BUF_MAX];
+ char *p = buf;
+
+ interrupt & MDM_DL1 ? p += snprintf(p, TMP_BUF_MAX, "MDM_DL1 ") : NULL;
+ interrupt & MDM_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "MDM_DL2 ") : NULL;
+
+ interrupt & MDM_UL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "MDM_UL1 ") : NULL;
+ interrupt & MDM_UL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "MDM_UL2 ") : NULL;
+
+ interrupt & DIAG_DL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "DIAG_DL1 ") : NULL;
+ interrupt & DIAG_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "DIAG_DL2 ") : NULL;
+
+ interrupt & DIAG_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "DIAG_UL ") : NULL;
+
+ interrupt & APP1_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "APP1_DL ") : NULL;
+ interrupt & APP2_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "APP2_DL ") : NULL;
+
+ interrupt & APP1_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "APP1_UL ") : NULL;
+ interrupt & APP2_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "APP2_UL ") : NULL;
+
+ interrupt & CTRL_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "CTRL_DL ") : NULL;
+ interrupt & CTRL_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "CTRL_UL ") : NULL;
+
+ interrupt & RESET ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+ "RESET ") : NULL;
+
+ return buf;
+}
+#endif
+
+/*
+ * Receive flow control
+ * Return 1 - If ok, else 0
+ */
+static int receive_flow_control(struct nozomi *dc)
+{
+ enum port_type port = PORT_MDM;
+ struct ctrl_dl ctrl_dl;
+ struct ctrl_dl old_ctrl;
+ u16 enable_ier = 0;
+
+ read_mem32((u32 *) &ctrl_dl, dc->port[PORT_CTRL].dl_addr[CH_A], 2);
+
+ switch (ctrl_dl.port) {
+ case CTRL_CMD:
+ DBG1("The Base Band sends this value as a response to a "
+ "request for IMSI detach sent over the control "
+ "channel uplink (see section 7.6.1).");
+ break;
+ case CTRL_MDM:
+ port = PORT_MDM;
+ enable_ier = MDM_DL;
+ break;
+ case CTRL_DIAG:
+ port = PORT_DIAG;
+ enable_ier = DIAG_DL;
+ break;
+ case CTRL_APP1:
+ port = PORT_APP1;
+ enable_ier = APP1_DL;
+ break;
+ case CTRL_APP2:
+ port = PORT_APP2;
+ enable_ier = APP2_DL;
+ break;
+ default:
+ dev_err(&dc->pdev->dev,
+ "ERROR: flow control received for non-existing port\n");
+ return 0;
+ };
+
+ DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl),
+ *((u16 *)&ctrl_dl));
+
+ old_ctrl = dc->port[port].ctrl_dl;
+ dc->port[port].ctrl_dl = ctrl_dl;
+
+ if (old_ctrl.CTS == 1 && ctrl_dl.CTS == 0) {
+ DBG1("Disable interrupt (0x%04X) on port: %d",
+ enable_ier, port);
+ disable_transmit_ul(port, dc);
+
+ } else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) {
+
+ if (__kfifo_len(dc->port[port].fifo_ul)) {
+ DBG1("Enable interrupt (0x%04X) on port: %d",
+ enable_ier, port);
+ DBG1("Data in buffer [%d], enable transmit! ",
+ __kfifo_len(dc->port[port].fifo_ul));
+ enable_transmit_ul(port, dc);
+ } else {
+ DBG1("No data in buffer...");
+ }
+ }
+
+ if (*(u16 *)&old_ctrl == *(u16 *)&ctrl_dl) {
+ DBG1(" No change in mctrl");
+ return 1;
+ }
+ /* Update statistics */
+ if (old_ctrl.CTS != ctrl_dl.CTS)
+ dc->port[port].tty_icount.cts++;
+ if (old_ctrl.DSR != ctrl_dl.DSR)
+ dc->port[port].tty_icount.dsr++;
+ if (old_ctrl.RI != ctrl_dl.RI)
+ dc->port[port].tty_icount.rng++;
+ if (old_ctrl.DCD != ctrl_dl.DCD)
+ dc->port[port].tty_icount.dcd++;
+
+ wake_up_interruptible(&dc->port[port].tty_wait);
+
+ DBG1("port: %d DCD(%d), CTS(%d), RI(%d), DSR(%d)",
+ port,
+ dc->port[port].tty_icount.dcd, dc->port[port].tty_icount.cts,
+ dc->port[port].tty_icount.rng, dc->port[port].tty_icount.dsr);
+
+ return 1;
+}
+
+static enum ctrl_port_type port2ctrl(enum port_type port,
+ const struct nozomi *dc)
+{
+ switch (port) {
+ case PORT_MDM:
+ return CTRL_MDM;
+ case PORT_DIAG:
+ return CTRL_DIAG;
+ case PORT_APP1:
+ return CTRL_APP1;
+ case PORT_APP2:
+ return CTRL_APP2;
+ default:
+ dev_err(&dc->pdev->dev,
+ "ERROR: send flow control " \
+ "received for non-existing port\n");
+ };
+ return CTRL_ERROR;
+}
+
+/*
+ * Send flow control, can only update one channel at a time
+ * Return 0 - If we have updated all flow control
+ * Return 1 - If we need to update more flow control, ack current enable more
+ */
+static int send_flow_control(struct nozomi *dc)
+{
+ u32 i, more_flow_control_to_be_updated = 0;
+ u16 *ctrl;
+
+ for (i = PORT_MDM; i < MAX_PORT; i++) {
+ if (dc->port[i].update_flow_control) {
+ if (more_flow_control_to_be_updated) {
+ /* We have more flow control to be updated */
+ return 1;
+ }
+ dc->port[i].ctrl_ul.port = port2ctrl(i, dc);
+ ctrl = (u16 *)&dc->port[i].ctrl_ul;
+ write_mem32(dc->port[PORT_CTRL].ul_addr[0], \
+ (u32 *) ctrl, 2);
+ dc->port[i].update_flow_control = 0;
+ more_flow_control_to_be_updated = 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Handle donlink data, ports that are handled are modem and diagnostics
+ * Return 1 - ok
+ * Return 0 - toggle fields are out of sync
+ */
+static int handle_data_dl(struct nozomi *dc, enum port_type port, u8 *toggle,
+ u16 read_iir, u16 mask1, u16 mask2)
+{
+ if (*toggle == 0 && read_iir & mask1) {
+ if (receive_data(port, dc)) {
+ writew(mask1, dc->reg_fcr);
+ *toggle = !(*toggle);
+ }
+
+ if (read_iir & mask2) {
+ if (receive_data(port, dc)) {
+ writew(mask2, dc->reg_fcr);
+ *toggle = !(*toggle);
+ }
+ }
+ } else if (*toggle == 1 && read_iir & mask2) {
+ if (receive_data(port, dc)) {
+ writew(mask2, dc->reg_fcr);
+ *toggle = !(*toggle);
+ }
+
+ if (read_iir & mask1) {
+ if (receive_data(port, dc)) {
+ writew(mask1, dc->reg_fcr);
+ *toggle = !(*toggle);
+ }
+ }
+ } else {
+ dev_err(&dc->pdev->dev, "port out of sync!, toggle:%d\n",
+ *toggle);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Handle uplink data, this is currently for the modem port
+ * Return 1 - ok
+ * Return 0 - toggle field are out of sync
+ */
+static int handle_data_ul(struct nozomi *dc, enum port_type port, u16 read_iir)
+{
+ u8 *toggle = &(dc->port[port].toggle_ul);
+
+ if (*toggle == 0 && read_iir & MDM_UL1) {
+ dc->last_ier &= ~MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_data(port, dc)) {
+ writew(MDM_UL1, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ *toggle = !*toggle;
+ }
+
+ if (read_iir & MDM_UL2) {
+ dc->last_ier &= ~MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_data(port, dc)) {
+ writew(MDM_UL2, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ *toggle = !*toggle;
+ }
+ }
+
+ } else if (*toggle == 1 && read_iir & MDM_UL2) {
+ dc->last_ier &= ~MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_data(port, dc)) {
+ writew(MDM_UL2, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ *toggle = !*toggle;
+ }
+
+ if (read_iir & MDM_UL1) {
+ dc->last_ier &= ~MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_data(port, dc)) {
+ writew(MDM_UL1, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | MDM_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ *toggle = !*toggle;
+ }
+ }
+ } else {
+ writew(read_iir & MDM_UL, dc->reg_fcr);
+ dev_err(&dc->pdev->dev, "port out of sync!\n");
+ return 0;
+ }
+ return 1;
+}
+
+static irqreturn_t interrupt_handler(int irq, void *dev_id)
+{
+ struct nozomi *dc = dev_id;
+ unsigned int a;
+ u16 read_iir;
+
+ if (!dc)
+ return IRQ_NONE;
+
+ spin_lock(&dc->spin_mutex);
+ read_iir = readw(dc->reg_iir);
+
+ /* Card removed */
+ if (read_iir == (u16)-1)
+ goto none;
+ /*
+ * Just handle interrupt enabled in IER
+ * (by masking with dc->last_ier)
+ */
+ read_iir &= dc->last_ier;
+
+ if (read_iir == 0)
+ goto none;
+
+
+ DBG4("%s irq:0x%04X, prev:0x%04X", interrupt2str(read_iir), read_iir,
+ dc->last_ier);
+
+ if (read_iir & RESET) {
+ if (unlikely(!nozomi_read_config_table(dc))) {
+ dc->last_ier = 0x0;
+ writew(dc->last_ier, dc->reg_ier);
+ dev_err(&dc->pdev->dev, "Could not read status from "
+ "card, we should disable interface\n");
+ } else {
+ writew(RESET, dc->reg_fcr);
+ }
+ /* No more useful info if this was the reset interrupt. */
+ goto exit_handler;
+ }
+ if (read_iir & CTRL_UL) {
+ DBG1("CTRL_UL");
+ dc->last_ier &= ~CTRL_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_flow_control(dc)) {
+ writew(CTRL_UL, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | CTRL_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ }
+ }
+ if (read_iir & CTRL_DL) {
+ receive_flow_control(dc);
+ writew(CTRL_DL, dc->reg_fcr);
+ }
+ if (read_iir & MDM_DL) {
+ if (!handle_data_dl(dc, PORT_MDM,
+ &(dc->port[PORT_MDM].toggle_dl), read_iir,
+ MDM_DL1, MDM_DL2)) {
+ dev_err(&dc->pdev->dev, "MDM_DL out of sync!\n");
+ goto exit_handler;
+ }
+ }
+ if (read_iir & MDM_UL) {
+ if (!handle_data_ul(dc, PORT_MDM, read_iir)) {
+ dev_err(&dc->pdev->dev, "MDM_UL out of sync!\n");
+ goto exit_handler;
+ }
+ }
+ if (read_iir & DIAG_DL) {
+ if (!handle_data_dl(dc, PORT_DIAG,
+ &(dc->port[PORT_DIAG].toggle_dl), read_iir,
+ DIAG_DL1, DIAG_DL2)) {
+ dev_err(&dc->pdev->dev, "DIAG_DL out of sync!\n");
+ goto exit_handler;
+ }
+ }
+ if (read_iir & DIAG_UL) {
+ dc->last_ier &= ~DIAG_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_data(PORT_DIAG, dc)) {
+ writew(DIAG_UL, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | DIAG_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ }
+ }
+ if (read_iir & APP1_DL) {
+ if (receive_data(PORT_APP1, dc))
+ writew(APP1_DL, dc->reg_fcr);
+ }
+ if (read_iir & APP1_UL) {
+ dc->last_ier &= ~APP1_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_data(PORT_APP1, dc)) {
+ writew(APP1_UL, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | APP1_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ }
+ }
+ if (read_iir & APP2_DL) {
+ if (receive_data(PORT_APP2, dc))
+ writew(APP2_DL, dc->reg_fcr);
+ }
+ if (read_iir & APP2_UL) {
+ dc->last_ier &= ~APP2_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ if (send_data(PORT_APP2, dc)) {
+ writew(APP2_UL, dc->reg_fcr);
+ dc->last_ier = dc->last_ier | APP2_UL;
+ writew(dc->last_ier, dc->reg_ier);
+ }
+ }
+
+exit_handler:
+ spin_unlock(&dc->spin_mutex);
+ for (a = 0; a < NOZOMI_MAX_PORTS; a++)
+ if (test_and_clear_bit(a, &dc->flip))
+ tty_flip_buffer_push(dc->port[a].tty);
+ return IRQ_HANDLED;
+none:
+ spin_unlock(&dc->spin_mutex);
+ return IRQ_NONE;
+}
+
+static void nozomi_get_card_type(struct nozomi *dc)
+{
+ int i;
+ u32 size = 0;
+
+ for (i = 0; i < 6; i++)
+ size += pci_resource_len(dc->pdev, i);
+
+ /* Assume card type F32_8 if no match */
+ dc->card_type = size == 2048 ? F32_2 : F32_8;
+
+ dev_info(&dc->pdev->dev, "Card type is: %d\n", dc->card_type);
+}
+
+static void nozomi_setup_private_data(struct nozomi *dc)
+{
+ void __iomem *offset = dc->base_addr + dc->card_type / 2;
+ unsigned int i;
+
+ dc->reg_fcr = (void __iomem *)(offset + R_FCR);
+ dc->reg_iir = (void __iomem *)(offset + R_IIR);
+ dc->reg_ier = (void __iomem *)(offset + R_IER);
+ dc->last_ier = 0;
+ dc->flip = 0;
+
+ dc->port[PORT_MDM].token_dl = MDM_DL;
+ dc->port[PORT_DIAG].token_dl = DIAG_DL;
+ dc->port[PORT_APP1].token_dl = APP1_DL;
+ dc->port[PORT_APP2].token_dl = APP2_DL;
+
+ for (i = 0; i < MAX_PORT; i++)
+ init_waitqueue_head(&dc->port[i].tty_wait);
+}
+
+static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+
+ return sprintf(buf, "%d\n", dc->card_type);
+}
+static DEVICE_ATTR(card_type, 0444, card_type_show, NULL);
+
+static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+
+ return sprintf(buf, "%u\n", dc->open_ttys);
+}
+static DEVICE_ATTR(open_ttys, 0444, open_ttys_show, NULL);
+
+static void make_sysfs_files(struct nozomi *dc)
+{
+ if (device_create_file(&dc->pdev->dev, &dev_attr_card_type))
+ dev_err(&dc->pdev->dev,
+ "Could not create sysfs file for card_type\n");
+ if (device_create_file(&dc->pdev->dev, &dev_attr_open_ttys))
+ dev_err(&dc->pdev->dev,
+ "Could not create sysfs file for open_ttys\n");
+}
+
+static void remove_sysfs_files(struct nozomi *dc)
+{
+ device_remove_file(&dc->pdev->dev, &dev_attr_card_type);
+ device_remove_file(&dc->pdev->dev, &dev_attr_open_ttys);
+}
+
+/* Allocate memory for one device */
+static int __devinit nozomi_card_init(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ resource_size_t start;
+ int ret;
+ struct nozomi *dc = NULL;
+ int ndev_idx;
+ int i;
+
+ dev_dbg(&pdev->dev, "Init, new card found\n");
+
+ for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++)
+ if (!ndevs[ndev_idx])
+ break;
+
+ if (ndev_idx >= ARRAY_SIZE(ndevs)) {
+ dev_err(&pdev->dev, "no free tty range for this card left\n");
+ ret = -EIO;
+ goto err;
+ }
+
+ dc = kzalloc(sizeof(struct nozomi), GFP_KERNEL);
+ if (unlikely(!dc)) {
+ dev_err(&pdev->dev, "Could not allocate memory\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ dc->pdev = pdev;
+
+ /* Find out what card type it is */
+ nozomi_get_card_type(dc);
+
+ ret = pci_enable_device(dc->pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable PCI Device\n");
+ goto err_free;
+ }
+
+ start = pci_resource_start(dc->pdev, 0);
+ if (start == 0) {
+ dev_err(&pdev->dev, "No I/O address for card detected\n");
+ ret = -ENODEV;
+ goto err_disable_device;
+ }
+
+ ret = pci_request_regions(dc->pdev, NOZOMI_NAME);
+ if (ret) {
+ dev_err(&pdev->dev, "I/O address 0x%04x already in use\n",
+ (int) /* nozomi_private.io_addr */ 0);
+ goto err_disable_device;
+ }
+
+ dc->base_addr = ioremap(start, dc->card_type);
+ if (!dc->base_addr) {
+ dev_err(&pdev->dev, "Unable to map card MMIO\n");
+ ret = -ENODEV;
+ goto err_rel_regs;
+ }
+
+ dc->send_buf = kmalloc(SEND_BUF_MAX, GFP_KERNEL);
+ if (!dc->send_buf) {
+ dev_err(&pdev->dev, "Could not allocate send buffer?\n");
+ ret = -ENOMEM;
+ goto err_free_sbuf;
+ }
+
+ spin_lock_init(&dc->spin_mutex);
+
+ nozomi_setup_private_data(dc);
+
+ /* Disable all interrupts */
+ dc->last_ier = 0;
+ writew(dc->last_ier, dc->reg_ier);
+
+ ret = request_irq(pdev->irq, &interrupt_handler, IRQF_SHARED,
+ NOZOMI_NAME, dc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq);
+ goto err_free_sbuf;
+ }
+
+ DBG1("base_addr: %p", dc->base_addr);
+
+ make_sysfs_files(dc);
+
+ dc->index_start = ndev_idx * MAX_PORT;
+ ndevs[ndev_idx] = dc;
+
+ for (i = 0; i < MAX_PORT; i++) {
+ mutex_init(&dc->port[i].tty_sem);
+ dc->port[i].tty_open_count = 0;
+ dc->port[i].tty = NULL;
+ tty_register_device(ntty_driver, dc->index_start + i,
+ &pdev->dev);
+ }
+
+ /* Enable RESET interrupt. */
+ dc->last_ier = RESET;
+ writew(dc->last_ier, dc->reg_ier);
+
+ pci_set_drvdata(pdev, dc);
+
+ return 0;
+
+err_free_sbuf:
+ kfree(dc->send_buf);
+ iounmap(dc->base_addr);
+err_rel_regs:
+ pci_release_regions(pdev);
+err_disable_device:
+ pci_disable_device(pdev);
+err_free:
+ kfree(dc);
+err:
+ return ret;
+}
+
+static void __devexit tty_exit(struct nozomi *dc)
+{
+ unsigned int i;
+
+ DBG1(" ");
+
+ flush_scheduled_work();
+
+ for (i = 0; i < MAX_PORT; ++i)
+ if (dc->port[i].tty && \
+ list_empty(&dc->port[i].tty->hangup_work.entry))
+ tty_hangup(dc->port[i].tty);
+
+ while (dc->open_ttys)
+ msleep(1);
+
+ for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
+ tty_unregister_device(ntty_driver, i);
+}
+
+/* Deallocate memory for one device */
+static void __devexit nozomi_card_exit(struct pci_dev *pdev)
+{
+ int i;
+ struct ctrl_ul ctrl;
+ struct nozomi *dc = pci_get_drvdata(pdev);
+
+ /* Disable all interrupts */
+ dc->last_ier = 0;
+ writew(dc->last_ier, dc->reg_ier);
+
+ tty_exit(dc);
+
+ /* Send 0x0001, command card to resend the reset token. */
+ /* This is to get the reset when the module is reloaded. */
+ ctrl.port = 0x00;
+ ctrl.reserved = 0;
+ ctrl.RTS = 0;
+ ctrl.DTR = 1;
+ DBG1("sending flow control 0x%04X", *((u16 *)&ctrl));
+
+ /* Setup dc->reg addresses to we can use defines here */
+ write_mem32(dc->port[PORT_CTRL].ul_addr[0], (u32 *)&ctrl, 2);
+ writew(CTRL_UL, dc->reg_fcr); /* push the token to the card. */
+
+ remove_sysfs_files(dc);
+
+ free_irq(pdev->irq, dc);
+
+ for (i = 0; i < MAX_PORT; i++)
+ if (dc->port[i].fifo_ul)
+ kfifo_free(dc->port[i].fifo_ul);
+
+ kfree(dc->send_buf);
+
+ iounmap(dc->base_addr);
+
+ pci_release_regions(pdev);
+
+ pci_disable_device(pdev);
+
+ ndevs[dc->index_start / MAX_PORT] = NULL;
+
+ kfree(dc);
+}
+
+static void set_rts(const struct tty_struct *tty, int rts)
+{
+ struct port *port = get_port_by_tty(tty);
+
+ port->ctrl_ul.RTS = rts;
+ port->update_flow_control = 1;
+ enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
+}
+
+static void set_dtr(const struct tty_struct *tty, int dtr)
+{
+ struct port *port = get_port_by_tty(tty);
+
+ DBG1("SETTING DTR index: %d, dtr: %d", tty->index, dtr);
+
+ port->ctrl_ul.DTR = dtr;
+ port->update_flow_control = 1;
+ enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * TTY code
+ * ----------------------------------------------------------------------------
+ */
+
+/* Called when the userspace process opens the tty, /dev/noz*. */
+static int ntty_open(struct tty_struct *tty, struct file *file)
+{
+ struct port *port = get_port_by_tty(tty);
+ struct nozomi *dc = get_dc_by_tty(tty);
+ unsigned long flags;
+
+ if (!port || !dc)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&port->tty_sem))
+ return -ERESTARTSYS;
+
+ port->tty_open_count++;
+ dc->open_ttys++;
+
+ /* Enable interrupt downlink for channel */
+ if (port->tty_open_count == 1) {
+ tty->low_latency = 1;
+ tty->driver_data = port;
+ port->tty = tty;
+ DBG1("open: %d", port->token_dl);
+ spin_lock_irqsave(&dc->spin_mutex, flags);
+ dc->last_ier = dc->last_ier | port->token_dl;
+ writew(dc->last_ier, dc->reg_ier);
+ spin_unlock_irqrestore(&dc->spin_mutex, flags);
+ }
+
+ mutex_unlock(&port->tty_sem);
+
+ return 0;
+}
+
+/* Called when the userspace process close the tty, /dev/noz*. */
+static void ntty_close(struct tty_struct *tty, struct file *file)
+{
+ struct nozomi *dc = get_dc_by_tty(tty);
+ struct port *port = tty->driver_data;
+ unsigned long flags;
+
+ if (!dc || !port)
+ return;
+
+ if (mutex_lock_interruptible(&port->tty_sem))
+ return;
+
+ if (!port->tty_open_count)
+ goto exit;
+
+ dc->open_ttys--;
+ port->tty_open_count--;
+
+ if (port->tty_open_count == 0) {
+ DBG1("close: %d", port->token_dl);
+ spin_lock_irqsave(&dc->spin_mutex, flags);
+ dc->last_ier &= ~(port->token_dl);
+ writew(dc->last_ier, dc->reg_ier);
+ spin_unlock_irqrestore(&dc->spin_mutex, flags);
+ }
+
+exit:
+ mutex_unlock(&port->tty_sem);
+}
+
+/*
+ * called when the userspace process writes to the tty (/dev/noz*).
+ * Data is inserted into a fifo, which is then read and transfered to the modem.
+ */
+static int ntty_write(struct tty_struct *tty, const unsigned char *buffer,
+ int count)
+{
+ int rval = -EINVAL;
+ struct nozomi *dc = get_dc_by_tty(tty);
+ struct port *port = tty->driver_data;
+ unsigned long flags;
+
+ /* DBG1( "WRITEx: %d, index = %d", count, index); */
+
+ if (!dc || !port)
+ return -ENODEV;
+
+ if (unlikely(!mutex_trylock(&port->tty_sem))) {
+ /*
+ * must test lock as tty layer wraps calls
+ * to this function with BKL
+ */
+ dev_err(&dc->pdev->dev, "Would have deadlocked - "
+ "return EAGAIN\n");
+ return -EAGAIN;
+ }
+
+ if (unlikely(!port->tty_open_count)) {
+ DBG1(" ");
+ goto exit;
+ }
+
+ rval = __kfifo_put(port->fifo_ul, (unsigned char *)buffer, count);
+
+ /* notify card */
+ if (unlikely(dc == NULL)) {
+ DBG1("No device context?");
+ goto exit;
+ }
+
+ spin_lock_irqsave(&dc->spin_mutex, flags);
+ /* CTS is only valid on the modem channel */
+ if (port == &(dc->port[PORT_MDM])) {
+ if (port->ctrl_dl.CTS) {
+ DBG4("Enable interrupt");
+ enable_transmit_ul(tty->index % MAX_PORT, dc);
+ } else {
+ dev_err(&dc->pdev->dev,
+ "CTS not active on modem port?\n");
+ }
+ } else {
+ enable_transmit_ul(tty->index % MAX_PORT, dc);
+ }
+ spin_unlock_irqrestore(&dc->spin_mutex, flags);
+
+exit:
+ mutex_unlock(&port->tty_sem);
+ return rval;
+}
+
+/*
+ * Calculate how much is left in device
+ * This method is called by the upper tty layer.
+ * #according to sources N_TTY.c it expects a value >= 0 and
+ * does not check for negative values.
+ */
+static int ntty_write_room(struct tty_struct *tty)
+{
+ struct port *port = tty->driver_data;
+ int room = 0;
+ struct nozomi *dc = get_dc_by_tty(tty);
+
+ if (!dc || !port)
+ return 0;
+ if (!mutex_trylock(&port->tty_sem))
+ return 0;
+
+ if (!port->tty_open_count)
+ goto exit;
+
+ room = port->fifo_ul->size - __kfifo_len(port->fifo_ul);
+
+exit:
+ mutex_unlock(&port->tty_sem);
+ return room;
+}
+
+/* Gets io control parameters */
+static int ntty_tiocmget(struct tty_struct *tty, struct file *file)
+{
+ struct port *port = tty->driver_data;
+ struct ctrl_dl *ctrl_dl = &port->ctrl_dl;
+ struct ctrl_ul *ctrl_ul = &port->ctrl_ul;
+
+ return (ctrl_ul->RTS ? TIOCM_RTS : 0) |
+ (ctrl_ul->DTR ? TIOCM_DTR : 0) |
+ (ctrl_dl->DCD ? TIOCM_CAR : 0) |
+ (ctrl_dl->RI ? TIOCM_RNG : 0) |
+ (ctrl_dl->DSR ? TIOCM_DSR : 0) |
+ (ctrl_dl->CTS ? TIOCM_CTS : 0);
+}
+
+/* Sets io controls parameters */
+static int ntty_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ if (set & TIOCM_RTS)
+ set_rts(tty, 1);
+ else if (clear & TIOCM_RTS)
+ set_rts(tty, 0);
+
+ if (set & TIOCM_DTR)
+ set_dtr(tty, 1);
+ else if (clear & TIOCM_DTR)
+ set_dtr(tty, 0);
+
+ return 0;
+}
+
+static int ntty_cflags_changed(struct port *port, unsigned long flags,
+ struct async_icount *cprev)
+{
+ struct async_icount cnow = port->tty_icount;
+ int ret;
+
+ ret = ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
+ ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) ||
+ ((flags & TIOCM_CD) && (cnow.dcd != cprev->dcd)) ||
+ ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts));
+
+ *cprev = cnow;
+
+ return ret;
+}
+
+static int ntty_ioctl_tiocgicount(struct port *port, void __user *argp)
+{
+ struct async_icount cnow = port->tty_icount;
+ struct serial_icounter_struct icount;
+
+ icount.cts = cnow.cts;
+ icount.dsr = cnow.dsr;
+ icount.rng = cnow.rng;
+ icount.dcd = cnow.dcd;
+ icount.rx = cnow.rx;
+ icount.tx = cnow.tx;
+ icount.frame = cnow.frame;
+ icount.overrun = cnow.overrun;
+ icount.parity = cnow.parity;
+ icount.brk = cnow.brk;
+ icount.buf_overrun = cnow.buf_overrun;
+
+ return copy_to_user(argp, &icount, sizeof(icount));
+}
+
+static int ntty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct port *port = tty->driver_data;
+ void __user *argp = (void __user *)arg;
+ int rval = -ENOIOCTLCMD;
+
+ DBG1("******** IOCTL, cmd: %d", cmd);
+
+ switch (cmd) {
+ case TIOCMIWAIT: {
+ struct async_icount cprev = port->tty_icount;
+
+ rval = wait_event_interruptible(port->tty_wait,
+ ntty_cflags_changed(port, arg, &cprev));
+ break;
+ } case TIOCGICOUNT:
+ rval = ntty_ioctl_tiocgicount(port, argp);
+ break;
+ default:
+ DBG1("ERR: 0x%08X, %d", cmd, cmd);
+ break;
+ };
+
+ return rval;
+}
+
+/*
+ * Called by the upper tty layer when tty buffers are ready
+ * to receive data again after a call to throttle.
+ */
+static void ntty_unthrottle(struct tty_struct *tty)
+{
+ struct nozomi *dc = get_dc_by_tty(tty);
+ unsigned long flags;
+
+ DBG1("UNTHROTTLE");
+ spin_lock_irqsave(&dc->spin_mutex, flags);
+ enable_transmit_dl(tty->index % MAX_PORT, dc);
+ set_rts(tty, 1);
+
+ spin_unlock_irqrestore(&dc->spin_mutex, flags);
+}
+
+/*
+ * Called by the upper tty layer when the tty buffers are almost full.
+ * The driver should stop send more data.
+ */
+static void ntty_throttle(struct tty_struct *tty)
+{
+ struct nozomi *dc = get_dc_by_tty(tty);
+ unsigned long flags;
+
+ DBG1("THROTTLE");
+ spin_lock_irqsave(&dc->spin_mutex, flags);
+ set_rts(tty, 0);
+ spin_unlock_irqrestore(&dc->spin_mutex, flags);
+}
+
+/* just to discard single character writes */
+static void ntty_put_char(struct tty_struct *tty, unsigned char c)
+{
+ /* FIXME !!! */
+ DBG2("PUT CHAR Function: %c", c);
+}
+
+/* Returns number of chars in buffer, called by tty layer */
+static s32 ntty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct port *port = tty->driver_data;
+ struct nozomi *dc = get_dc_by_tty(tty);
+ s32 rval;
+
+ if (unlikely(!dc || !port)) {
+ rval = -ENODEV;
+ goto exit_in_buffer;
+ }
+
+ if (unlikely(!port->tty_open_count)) {
+ dev_err(&dc->pdev->dev, "No tty open?\n");
+ rval = -ENODEV;
+ goto exit_in_buffer;
+ }
+
+ rval = __kfifo_len(port->fifo_ul);
+
+exit_in_buffer:
+ return rval;
+}
+
+static struct tty_operations tty_ops = {
+ .ioctl = ntty_ioctl,
+ .open = ntty_open,
+ .close = ntty_close,
+ .write = ntty_write,
+ .write_room = ntty_write_room,
+ .unthrottle = ntty_unthrottle,
+ .throttle = ntty_throttle,
+ .chars_in_buffer = ntty_chars_in_buffer,
+ .put_char = ntty_put_char,
+ .tiocmget = ntty_tiocmget,
+ .tiocmset = ntty_tiocmset,
+};
+
+/* Module initialization */
+static struct pci_driver nozomi_driver = {
+ .name = NOZOMI_NAME,
+ .id_table = nozomi_pci_tbl,
+ .probe = nozomi_card_init,
+ .remove = __devexit_p(nozomi_card_exit),
+};
+
+static __init int nozomi_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Initializing %s\n", VERSION_STRING);
+
+ ntty_driver = alloc_tty_driver(NTTY_TTY_MAXMINORS);
+ if (!ntty_driver)
+ return -ENOMEM;
+
+ ntty_driver->owner = THIS_MODULE;
+ ntty_driver->driver_name = NOZOMI_NAME_TTY;
+ ntty_driver->name = "noz";
+ ntty_driver->major = 0;
+ ntty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ ntty_driver->subtype = SERIAL_TYPE_NORMAL;
+ ntty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ ntty_driver->init_termios = tty_std_termios;
+ ntty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | \
+ HUPCL | CLOCAL;
+ ntty_driver->init_termios.c_ispeed = 115200;
+ ntty_driver->init_termios.c_ospeed = 115200;
+ tty_set_operations(ntty_driver, &tty_ops);
+
+ ret = tty_register_driver(ntty_driver);
+ if (ret) {
+ printk(KERN_ERR "Nozomi: failed to register ntty driver\n");
+ goto free_tty;
+ }
+
+ ret = pci_register_driver(&nozomi_driver);
+ if (ret) {
+ printk(KERN_ERR "Nozomi: can't register pci driver\n");
+ goto unr_tty;
+ }
+
+ return 0;
+unr_tty:
+ tty_unregister_driver(ntty_driver);
+free_tty:
+ put_tty_driver(ntty_driver);
+ return ret;
+}
+
+static __exit void nozomi_exit(void)
+{
+ printk(KERN_INFO "Unloading %s\n", DRIVER_DESC);
+ pci_unregister_driver(&nozomi_driver);
+ tty_unregister_driver(ntty_driver);
+ put_tty_driver(ntty_driver);
+}
+
+module_init(nozomi_init);
+module_exit(nozomi_exit);
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 79581fab82d6..5efd5550f4ca 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -828,11 +828,8 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
/* prepare interface data */
- policy->kobj.parent = &sys_dev->kobj;
- policy->kobj.ktype = &ktype_cpufreq;
- kobject_set_name(&policy->kobj, "cpufreq");
-
- ret = kobject_register(&policy->kobj);
+ ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &sys_dev->kobj,
+ "cpufreq");
if (ret) {
unlock_policy_rwsem_write(cpu);
goto err_out_driver_exit;
@@ -902,6 +899,7 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
goto err_out_unregister;
}
+ kobject_uevent(&policy->kobj, KOBJ_ADD);
module_put(cpufreq_driver->owner);
dprintk("initialization complete\n");
cpufreq_debug_enable_ratelimit();
@@ -915,7 +913,7 @@ err_out_unregister:
cpufreq_cpu_data[j] = NULL;
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
- kobject_unregister(&policy->kobj);
+ kobject_put(&policy->kobj);
wait_for_completion(&policy->kobj_unregister);
err_out_driver_exit:
@@ -1032,8 +1030,6 @@ static int __cpufreq_remove_dev (struct sys_device * sys_dev)
unlock_policy_rwsem_write(cpu);
- kobject_unregister(&data->kobj);
-
kobject_put(&data->kobj);
/* we need to make sure that the underlying kobj is actually
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index 0f3515e77d4b..088ea74edd34 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -277,7 +277,7 @@ static struct kobj_type ktype_state_cpuidle = {
static void inline cpuidle_free_state_kobj(struct cpuidle_device *device, int i)
{
- kobject_unregister(&device->kobjs[i]->kobj);
+ kobject_put(&device->kobjs[i]->kobj);
wait_for_completion(&device->kobjs[i]->kobj_unregister);
kfree(device->kobjs[i]);
device->kobjs[i] = NULL;
@@ -300,14 +300,13 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device)
kobj->state = &device->states[i];
init_completion(&kobj->kobj_unregister);
- kobj->kobj.parent = &device->kobj;
- kobj->kobj.ktype = &ktype_state_cpuidle;
- kobject_set_name(&kobj->kobj, "state%d", i);
- ret = kobject_register(&kobj->kobj);
+ ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &device->kobj,
+ "state%d", i);
if (ret) {
kfree(kobj);
goto error_state;
}
+ kobject_uevent(&kobj->kobj, KOBJ_ADD);
device->kobjs[i] = kobj;
}
@@ -339,12 +338,14 @@ int cpuidle_add_sysfs(struct sys_device *sysdev)
{
int cpu = sysdev->id;
struct cpuidle_device *dev;
+ int error;
dev = per_cpu(cpuidle_devices, cpu);
- dev->kobj.parent = &sysdev->kobj;
- dev->kobj.ktype = &ktype_cpuidle;
- kobject_set_name(&dev->kobj, "%s", "cpuidle");
- return kobject_register(&dev->kobj);
+ error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &sysdev->kobj,
+ "cpuidle");
+ if (!error)
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+ return error;
}
/**
@@ -357,5 +358,5 @@ void cpuidle_remove_sysfs(struct sys_device *sysdev)
struct cpuidle_device *dev;
dev = per_cpu(cpuidle_devices, cpu);
- kobject_unregister(&dev->kobj);
+ kobject_put(&dev->kobj);
}
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index ddd3a259cea1..74bd599dfb0c 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -83,4 +83,15 @@ config ZCRYPT_MONOLITHIC
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
+config CRYPTO_DEV_HIFN_795X
+ tristate "Driver HIFN 795x crypto accelerator chips"
+ select CRYPTO_DES
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ depends on PCI
+ help
+ This option allows you to have support for HIFN 795x crypto adapters.
+
+
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index d070030f7d7e..c0327f0dadc5 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o
obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
+obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
diff --git a/drivers/crypto/geode-aes.c b/drivers/crypto/geode-aes.c
index 711e246e1ef0..4801162919d9 100644
--- a/drivers/crypto/geode-aes.c
+++ b/drivers/crypto/geode-aes.c
@@ -13,44 +13,13 @@
#include <linux/crypto.h>
#include <linux/spinlock.h>
#include <crypto/algapi.h>
+#include <crypto/aes.h>
#include <asm/io.h>
#include <asm/delay.h>
#include "geode-aes.h"
-/* Register definitions */
-
-#define AES_CTRLA_REG 0x0000
-
-#define AES_CTRL_START 0x01
-#define AES_CTRL_DECRYPT 0x00
-#define AES_CTRL_ENCRYPT 0x02
-#define AES_CTRL_WRKEY 0x04
-#define AES_CTRL_DCA 0x08
-#define AES_CTRL_SCA 0x10
-#define AES_CTRL_CBC 0x20
-
-#define AES_INTR_REG 0x0008
-
-#define AES_INTRA_PENDING (1 << 16)
-#define AES_INTRB_PENDING (1 << 17)
-
-#define AES_INTR_PENDING (AES_INTRA_PENDING | AES_INTRB_PENDING)
-#define AES_INTR_MASK 0x07
-
-#define AES_SOURCEA_REG 0x0010
-#define AES_DSTA_REG 0x0014
-#define AES_LENA_REG 0x0018
-#define AES_WRITEKEY0_REG 0x0030
-#define AES_WRITEIV0_REG 0x0040
-
-/* A very large counter that is used to gracefully bail out of an
- * operation in case of trouble
- */
-
-#define AES_OP_TIMEOUT 0x50000
-
/* Static structures */
static void __iomem * _iobase;
@@ -87,9 +56,10 @@ do_crypt(void *src, void *dst, int len, u32 flags)
/* Start the operation */
iowrite32(AES_CTRL_START | flags, _iobase + AES_CTRLA_REG);
- do
+ do {
status = ioread32(_iobase + AES_INTR_REG);
- while(!(status & AES_INTRA_PENDING) && --counter);
+ cpu_relax();
+ } while(!(status & AES_INTRA_PENDING) && --counter);
/* Clear the event */
iowrite32((status & 0xFF) | AES_INTRA_PENDING, _iobase + AES_INTR_REG);
@@ -101,6 +71,7 @@ geode_aes_crypt(struct geode_aes_op *op)
{
u32 flags = 0;
unsigned long iflags;
+ int ret;
if (op->len == 0)
return 0;
@@ -129,7 +100,8 @@ geode_aes_crypt(struct geode_aes_op *op)
_writefield(AES_WRITEKEY0_REG, op->key);
}
- do_crypt(op->src, op->dst, op->len, flags);
+ ret = do_crypt(op->src, op->dst, op->len, flags);
+ BUG_ON(ret);
if (op->mode == AES_MODE_CBC)
_readfield(AES_WRITEIV0_REG, op->iv);
@@ -141,18 +113,103 @@ geode_aes_crypt(struct geode_aes_op *op)
/* CRYPTO-API Functions */
-static int
-geode_setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int len)
+static int geode_setkey_cip(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int len)
{
struct geode_aes_op *op = crypto_tfm_ctx(tfm);
+ unsigned int ret;
- if (len != AES_KEY_LENGTH) {
+ op->keylen = len;
+
+ if (len == AES_KEYSIZE_128) {
+ memcpy(op->key, key, len);
+ return 0;
+ }
+
+ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
+ /* not supported at all */
tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
return -EINVAL;
}
- memcpy(op->key, key, len);
- return 0;
+ /*
+ * The requested key size is not supported by HW, do a fallback
+ */
+ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ op->fallback.blk->base.crt_flags |= (tfm->crt_flags & CRYPTO_TFM_REQ_MASK);
+
+ ret = crypto_cipher_setkey(op->fallback.cip, key, len);
+ if (ret) {
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |= (op->fallback.blk->base.crt_flags & CRYPTO_TFM_RES_MASK);
+ }
+ return ret;
+}
+
+static int geode_setkey_blk(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int len)
+{
+ struct geode_aes_op *op = crypto_tfm_ctx(tfm);
+ unsigned int ret;
+
+ op->keylen = len;
+
+ if (len == AES_KEYSIZE_128) {
+ memcpy(op->key, key, len);
+ return 0;
+ }
+
+ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
+ /* not supported at all */
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ /*
+ * The requested key size is not supported by HW, do a fallback
+ */
+ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ op->fallback.blk->base.crt_flags |= (tfm->crt_flags & CRYPTO_TFM_REQ_MASK);
+
+ ret = crypto_blkcipher_setkey(op->fallback.blk, key, len);
+ if (ret) {
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |= (op->fallback.blk->base.crt_flags & CRYPTO_TFM_RES_MASK);
+ }
+ return ret;
+}
+
+static int fallback_blk_dec(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ unsigned int ret;
+ struct crypto_blkcipher *tfm;
+ struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
+
+ tfm = desc->tfm;
+ desc->tfm = op->fallback.blk;
+
+ ret = crypto_blkcipher_decrypt_iv(desc, dst, src, nbytes);
+
+ desc->tfm = tfm;
+ return ret;
+}
+static int fallback_blk_enc(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ unsigned int ret;
+ struct crypto_blkcipher *tfm;
+ struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
+
+ tfm = desc->tfm;
+ desc->tfm = op->fallback.blk;
+
+ ret = crypto_blkcipher_encrypt_iv(desc, dst, src, nbytes);
+
+ desc->tfm = tfm;
+ return ret;
}
static void
@@ -160,8 +217,10 @@ geode_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
struct geode_aes_op *op = crypto_tfm_ctx(tfm);
- if ((out == NULL) || (in == NULL))
+ if (unlikely(op->keylen != AES_KEYSIZE_128)) {
+ crypto_cipher_encrypt_one(op->fallback.cip, out, in);
return;
+ }
op->src = (void *) in;
op->dst = (void *) out;
@@ -179,8 +238,10 @@ geode_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
struct geode_aes_op *op = crypto_tfm_ctx(tfm);
- if ((out == NULL) || (in == NULL))
+ if (unlikely(op->keylen != AES_KEYSIZE_128)) {
+ crypto_cipher_decrypt_one(op->fallback.cip, out, in);
return;
+ }
op->src = (void *) in;
op->dst = (void *) out;
@@ -192,24 +253,50 @@ geode_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
geode_aes_crypt(op);
}
+static int fallback_init_cip(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct geode_aes_op *op = crypto_tfm_ctx(tfm);
+
+ op->fallback.cip = crypto_alloc_cipher(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+
+ if (IS_ERR(op->fallback.cip)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.blk);
+ }
+
+ return 0;
+}
+
+static void fallback_exit_cip(struct crypto_tfm *tfm)
+{
+ struct geode_aes_op *op = crypto_tfm_ctx(tfm);
+
+ crypto_free_cipher(op->fallback.cip);
+ op->fallback.cip = NULL;
+}
static struct crypto_alg geode_alg = {
- .cra_name = "aes",
- .cra_driver_name = "geode-aes-128",
- .cra_priority = 300,
- .cra_alignmask = 15,
- .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_name = "aes",
+ .cra_driver_name = "geode-aes",
+ .cra_priority = 300,
+ .cra_alignmask = 15,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_cip,
+ .cra_exit = fallback_exit_cip,
.cra_blocksize = AES_MIN_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct geode_aes_op),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(geode_alg.cra_list),
- .cra_u = {
- .cipher = {
- .cia_min_keysize = AES_KEY_LENGTH,
- .cia_max_keysize = AES_KEY_LENGTH,
- .cia_setkey = geode_setkey,
- .cia_encrypt = geode_encrypt,
- .cia_decrypt = geode_decrypt
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(geode_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = geode_setkey_cip,
+ .cia_encrypt = geode_encrypt,
+ .cia_decrypt = geode_decrypt
}
}
};
@@ -223,8 +310,12 @@ geode_cbc_decrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err, ret;
+ if (unlikely(op->keylen != AES_KEYSIZE_128))
+ return fallback_blk_dec(desc, dst, src, nbytes);
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
+ op->iv = walk.iv;
while((nbytes = walk.nbytes)) {
op->src = walk.src.virt.addr,
@@ -233,13 +324,9 @@ geode_cbc_decrypt(struct blkcipher_desc *desc,
op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
op->dir = AES_DIR_DECRYPT;
- memcpy(op->iv, walk.iv, AES_IV_LENGTH);
-
ret = geode_aes_crypt(op);
- memcpy(walk.iv, op->iv, AES_IV_LENGTH);
nbytes -= ret;
-
err = blkcipher_walk_done(desc, &walk, nbytes);
}
@@ -255,8 +342,12 @@ geode_cbc_encrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err, ret;
+ if (unlikely(op->keylen != AES_KEYSIZE_128))
+ return fallback_blk_enc(desc, dst, src, nbytes);
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
+ op->iv = walk.iv;
while((nbytes = walk.nbytes)) {
op->src = walk.src.virt.addr,
@@ -265,8 +356,6 @@ geode_cbc_encrypt(struct blkcipher_desc *desc,
op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
op->dir = AES_DIR_ENCRYPT;
- memcpy(op->iv, walk.iv, AES_IV_LENGTH);
-
ret = geode_aes_crypt(op);
nbytes -= ret;
err = blkcipher_walk_done(desc, &walk, nbytes);
@@ -275,22 +364,49 @@ geode_cbc_encrypt(struct blkcipher_desc *desc,
return err;
}
+static int fallback_init_blk(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct geode_aes_op *op = crypto_tfm_ctx(tfm);
+
+ op->fallback.blk = crypto_alloc_blkcipher(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+
+ if (IS_ERR(op->fallback.blk)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.blk);
+ }
+
+ return 0;
+}
+
+static void fallback_exit_blk(struct crypto_tfm *tfm)
+{
+ struct geode_aes_op *op = crypto_tfm_ctx(tfm);
+
+ crypto_free_blkcipher(op->fallback.blk);
+ op->fallback.blk = NULL;
+}
+
static struct crypto_alg geode_cbc_alg = {
.cra_name = "cbc(aes)",
- .cra_driver_name = "cbc-aes-geode-128",
+ .cra_driver_name = "cbc-aes-geode",
.cra_priority = 400,
- .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_blk,
+ .cra_exit = fallback_exit_blk,
.cra_blocksize = AES_MIN_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct geode_aes_op),
.cra_alignmask = 15,
- .cra_type = &crypto_blkcipher_type,
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(geode_cbc_alg.cra_list),
- .cra_u = {
- .blkcipher = {
- .min_keysize = AES_KEY_LENGTH,
- .max_keysize = AES_KEY_LENGTH,
- .setkey = geode_setkey,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(geode_cbc_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = geode_setkey_blk,
.encrypt = geode_cbc_encrypt,
.decrypt = geode_cbc_decrypt,
.ivsize = AES_IV_LENGTH,
@@ -307,6 +423,9 @@ geode_ecb_decrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err, ret;
+ if (unlikely(op->keylen != AES_KEYSIZE_128))
+ return fallback_blk_dec(desc, dst, src, nbytes);
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -334,6 +453,9 @@ geode_ecb_encrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err, ret;
+ if (unlikely(op->keylen != AES_KEYSIZE_128))
+ return fallback_blk_enc(desc, dst, src, nbytes);
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -353,28 +475,31 @@ geode_ecb_encrypt(struct blkcipher_desc *desc,
}
static struct crypto_alg geode_ecb_alg = {
- .cra_name = "ecb(aes)",
- .cra_driver_name = "ecb-aes-geode-128",
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "ecb-aes-geode",
.cra_priority = 400,
- .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_blk,
+ .cra_exit = fallback_exit_blk,
.cra_blocksize = AES_MIN_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct geode_aes_op),
.cra_alignmask = 15,
- .cra_type = &crypto_blkcipher_type,
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(geode_ecb_alg.cra_list),
- .cra_u = {
- .blkcipher = {
- .min_keysize = AES_KEY_LENGTH,
- .max_keysize = AES_KEY_LENGTH,
- .setkey = geode_setkey,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(geode_ecb_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = geode_setkey_blk,
.encrypt = geode_ecb_encrypt,
.decrypt = geode_ecb_decrypt,
}
}
};
-static void
+static void __devexit
geode_aes_remove(struct pci_dev *dev)
{
crypto_unregister_alg(&geode_alg);
@@ -389,7 +514,7 @@ geode_aes_remove(struct pci_dev *dev)
}
-static int
+static int __devinit
geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int ret;
@@ -397,7 +522,7 @@ geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id)
if ((ret = pci_enable_device(dev)))
return ret;
- if ((ret = pci_request_regions(dev, "geode-aes-128")))
+ if ((ret = pci_request_regions(dev, "geode-aes")))
goto eenable;
_iobase = pci_iomap(dev, 0, 0);
@@ -472,7 +597,6 @@ geode_aes_exit(void)
MODULE_AUTHOR("Advanced Micro Devices, Inc.");
MODULE_DESCRIPTION("Geode LX Hardware AES driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("aes");
module_init(geode_aes_init);
module_exit(geode_aes_exit);
diff --git a/drivers/crypto/geode-aes.h b/drivers/crypto/geode-aes.h
index f47968671ae7..f1855b50da48 100644
--- a/drivers/crypto/geode-aes.h
+++ b/drivers/crypto/geode-aes.h
@@ -9,9 +9,9 @@
#ifndef _GEODE_AES_H_
#define _GEODE_AES_H_
-#define AES_KEY_LENGTH 16
+/* driver logic flags */
#define AES_IV_LENGTH 16
-
+#define AES_KEY_LENGTH 16
#define AES_MIN_BLOCK_SIZE 16
#define AES_MODE_ECB 0
@@ -22,6 +22,38 @@
#define AES_FLAGS_HIDDENKEY (1 << 0)
+/* Register definitions */
+
+#define AES_CTRLA_REG 0x0000
+
+#define AES_CTRL_START 0x01
+#define AES_CTRL_DECRYPT 0x00
+#define AES_CTRL_ENCRYPT 0x02
+#define AES_CTRL_WRKEY 0x04
+#define AES_CTRL_DCA 0x08
+#define AES_CTRL_SCA 0x10
+#define AES_CTRL_CBC 0x20
+
+#define AES_INTR_REG 0x0008
+
+#define AES_INTRA_PENDING (1 << 16)
+#define AES_INTRB_PENDING (1 << 17)
+
+#define AES_INTR_PENDING (AES_INTRA_PENDING | AES_INTRB_PENDING)
+#define AES_INTR_MASK 0x07
+
+#define AES_SOURCEA_REG 0x0010
+#define AES_DSTA_REG 0x0014
+#define AES_LENA_REG 0x0018
+#define AES_WRITEKEY0_REG 0x0030
+#define AES_WRITEIV0_REG 0x0040
+
+/* A very large counter that is used to gracefully bail out of an
+ * operation in case of trouble
+ */
+
+#define AES_OP_TIMEOUT 0x50000
+
struct geode_aes_op {
void *src;
@@ -33,7 +65,13 @@ struct geode_aes_op {
int len;
u8 key[AES_KEY_LENGTH];
- u8 iv[AES_IV_LENGTH];
+ u8 *iv;
+
+ union {
+ struct crypto_blkcipher *blk;
+ struct crypto_cipher *cip;
+ } fallback;
+ u32 keylen;
};
#endif
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
new file mode 100644
index 000000000000..16413e57597c
--- /dev/null
+++ b/drivers/crypto/hifn_795x.c
@@ -0,0 +1,2838 @@
+/*
+ * 2007+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/crypto.h>
+#include <linux/hw_random.h>
+#include <linux/ktime.h>
+
+#include <crypto/algapi.h>
+#include <crypto/des.h>
+
+#include <asm/kmap_types.h>
+
+#undef dprintk
+
+#define HIFN_TEST
+//#define HIFN_DEBUG
+
+#ifdef HIFN_DEBUG
+#define dprintk(f, a...) printk(f, ##a)
+#else
+#define dprintk(f, a...) do {} while (0)
+#endif
+
+static char hifn_pll_ref[sizeof("extNNN")] = "ext";
+module_param_string(hifn_pll_ref, hifn_pll_ref, sizeof(hifn_pll_ref), 0444);
+MODULE_PARM_DESC(hifn_pll_ref,
+ "PLL reference clock (pci[freq] or ext[freq], default ext)");
+
+static atomic_t hifn_dev_number;
+
+#define ACRYPTO_OP_DECRYPT 0
+#define ACRYPTO_OP_ENCRYPT 1
+#define ACRYPTO_OP_HMAC 2
+#define ACRYPTO_OP_RNG 3
+
+#define ACRYPTO_MODE_ECB 0
+#define ACRYPTO_MODE_CBC 1
+#define ACRYPTO_MODE_CFB 2
+#define ACRYPTO_MODE_OFB 3
+
+#define ACRYPTO_TYPE_AES_128 0
+#define ACRYPTO_TYPE_AES_192 1
+#define ACRYPTO_TYPE_AES_256 2
+#define ACRYPTO_TYPE_3DES 3
+#define ACRYPTO_TYPE_DES 4
+
+#define PCI_VENDOR_ID_HIFN 0x13A3
+#define PCI_DEVICE_ID_HIFN_7955 0x0020
+#define PCI_DEVICE_ID_HIFN_7956 0x001d
+
+/* I/O region sizes */
+
+#define HIFN_BAR0_SIZE 0x1000
+#define HIFN_BAR1_SIZE 0x2000
+#define HIFN_BAR2_SIZE 0x8000
+
+/* DMA registres */
+
+#define HIFN_DMA_CRA 0x0C /* DMA Command Ring Address */
+#define HIFN_DMA_SDRA 0x1C /* DMA Source Data Ring Address */
+#define HIFN_DMA_RRA 0x2C /* DMA Result Ring Address */
+#define HIFN_DMA_DDRA 0x3C /* DMA Destination Data Ring Address */
+#define HIFN_DMA_STCTL 0x40 /* DMA Status and Control */
+#define HIFN_DMA_INTREN 0x44 /* DMA Interrupt Enable */
+#define HIFN_DMA_CFG1 0x48 /* DMA Configuration #1 */
+#define HIFN_DMA_CFG2 0x6C /* DMA Configuration #2 */
+#define HIFN_CHIP_ID 0x98 /* Chip ID */
+
+/*
+ * Processing Unit Registers (offset from BASEREG0)
+ */
+#define HIFN_0_PUDATA 0x00 /* Processing Unit Data */
+#define HIFN_0_PUCTRL 0x04 /* Processing Unit Control */
+#define HIFN_0_PUISR 0x08 /* Processing Unit Interrupt Status */
+#define HIFN_0_PUCNFG 0x0c /* Processing Unit Configuration */
+#define HIFN_0_PUIER 0x10 /* Processing Unit Interrupt Enable */
+#define HIFN_0_PUSTAT 0x14 /* Processing Unit Status/Chip ID */
+#define HIFN_0_FIFOSTAT 0x18 /* FIFO Status */
+#define HIFN_0_FIFOCNFG 0x1c /* FIFO Configuration */
+#define HIFN_0_SPACESIZE 0x20 /* Register space size */
+
+/* Processing Unit Control Register (HIFN_0_PUCTRL) */
+#define HIFN_PUCTRL_CLRSRCFIFO 0x0010 /* clear source fifo */
+#define HIFN_PUCTRL_STOP 0x0008 /* stop pu */
+#define HIFN_PUCTRL_LOCKRAM 0x0004 /* lock ram */
+#define HIFN_PUCTRL_DMAENA 0x0002 /* enable dma */
+#define HIFN_PUCTRL_RESET 0x0001 /* Reset processing unit */
+
+/* Processing Unit Interrupt Status Register (HIFN_0_PUISR) */
+#define HIFN_PUISR_CMDINVAL 0x8000 /* Invalid command interrupt */
+#define HIFN_PUISR_DATAERR 0x4000 /* Data error interrupt */
+#define HIFN_PUISR_SRCFIFO 0x2000 /* Source FIFO ready interrupt */
+#define HIFN_PUISR_DSTFIFO 0x1000 /* Destination FIFO ready interrupt */
+#define HIFN_PUISR_DSTOVER 0x0200 /* Destination overrun interrupt */
+#define HIFN_PUISR_SRCCMD 0x0080 /* Source command interrupt */
+#define HIFN_PUISR_SRCCTX 0x0040 /* Source context interrupt */
+#define HIFN_PUISR_SRCDATA 0x0020 /* Source data interrupt */
+#define HIFN_PUISR_DSTDATA 0x0010 /* Destination data interrupt */
+#define HIFN_PUISR_DSTRESULT 0x0004 /* Destination result interrupt */
+
+/* Processing Unit Configuration Register (HIFN_0_PUCNFG) */
+#define HIFN_PUCNFG_DRAMMASK 0xe000 /* DRAM size mask */
+#define HIFN_PUCNFG_DSZ_256K 0x0000 /* 256k dram */
+#define HIFN_PUCNFG_DSZ_512K 0x2000 /* 512k dram */
+#define HIFN_PUCNFG_DSZ_1M 0x4000 /* 1m dram */
+#define HIFN_PUCNFG_DSZ_2M 0x6000 /* 2m dram */
+#define HIFN_PUCNFG_DSZ_4M 0x8000 /* 4m dram */
+#define HIFN_PUCNFG_DSZ_8M 0xa000 /* 8m dram */
+#define HIFN_PUNCFG_DSZ_16M 0xc000 /* 16m dram */
+#define HIFN_PUCNFG_DSZ_32M 0xe000 /* 32m dram */
+#define HIFN_PUCNFG_DRAMREFRESH 0x1800 /* DRAM refresh rate mask */
+#define HIFN_PUCNFG_DRFR_512 0x0000 /* 512 divisor of ECLK */
+#define HIFN_PUCNFG_DRFR_256 0x0800 /* 256 divisor of ECLK */
+#define HIFN_PUCNFG_DRFR_128 0x1000 /* 128 divisor of ECLK */
+#define HIFN_PUCNFG_TCALLPHASES 0x0200 /* your guess is as good as mine... */
+#define HIFN_PUCNFG_TCDRVTOTEM 0x0100 /* your guess is as good as mine... */
+#define HIFN_PUCNFG_BIGENDIAN 0x0080 /* DMA big endian mode */
+#define HIFN_PUCNFG_BUS32 0x0040 /* Bus width 32bits */
+#define HIFN_PUCNFG_BUS16 0x0000 /* Bus width 16 bits */
+#define HIFN_PUCNFG_CHIPID 0x0020 /* Allow chipid from PUSTAT */
+#define HIFN_PUCNFG_DRAM 0x0010 /* Context RAM is DRAM */
+#define HIFN_PUCNFG_SRAM 0x0000 /* Context RAM is SRAM */
+#define HIFN_PUCNFG_COMPSING 0x0004 /* Enable single compression context */
+#define HIFN_PUCNFG_ENCCNFG 0x0002 /* Encryption configuration */
+
+/* Processing Unit Interrupt Enable Register (HIFN_0_PUIER) */
+#define HIFN_PUIER_CMDINVAL 0x8000 /* Invalid command interrupt */
+#define HIFN_PUIER_DATAERR 0x4000 /* Data error interrupt */
+#define HIFN_PUIER_SRCFIFO 0x2000 /* Source FIFO ready interrupt */
+#define HIFN_PUIER_DSTFIFO 0x1000 /* Destination FIFO ready interrupt */
+#define HIFN_PUIER_DSTOVER 0x0200 /* Destination overrun interrupt */
+#define HIFN_PUIER_SRCCMD 0x0080 /* Source command interrupt */
+#define HIFN_PUIER_SRCCTX 0x0040 /* Source context interrupt */
+#define HIFN_PUIER_SRCDATA 0x0020 /* Source data interrupt */
+#define HIFN_PUIER_DSTDATA 0x0010 /* Destination data interrupt */
+#define HIFN_PUIER_DSTRESULT 0x0004 /* Destination result interrupt */
+
+/* Processing Unit Status Register/Chip ID (HIFN_0_PUSTAT) */
+#define HIFN_PUSTAT_CMDINVAL 0x8000 /* Invalid command interrupt */
+#define HIFN_PUSTAT_DATAERR 0x4000 /* Data error interrupt */
+#define HIFN_PUSTAT_SRCFIFO 0x2000 /* Source FIFO ready interrupt */
+#define HIFN_PUSTAT_DSTFIFO 0x1000 /* Destination FIFO ready interrupt */
+#define HIFN_PUSTAT_DSTOVER 0x0200 /* Destination overrun interrupt */
+#define HIFN_PUSTAT_SRCCMD 0x0080 /* Source command interrupt */
+#define HIFN_PUSTAT_SRCCTX 0x0040 /* Source context interrupt */
+#define HIFN_PUSTAT_SRCDATA 0x0020 /* Source data interrupt */
+#define HIFN_PUSTAT_DSTDATA 0x0010 /* Destination data interrupt */
+#define HIFN_PUSTAT_DSTRESULT 0x0004 /* Destination result interrupt */
+#define HIFN_PUSTAT_CHIPREV 0x00ff /* Chip revision mask */
+#define HIFN_PUSTAT_CHIPENA 0xff00 /* Chip enabled mask */
+#define HIFN_PUSTAT_ENA_2 0x1100 /* Level 2 enabled */
+#define HIFN_PUSTAT_ENA_1 0x1000 /* Level 1 enabled */
+#define HIFN_PUSTAT_ENA_0 0x3000 /* Level 0 enabled */
+#define HIFN_PUSTAT_REV_2 0x0020 /* 7751 PT6/2 */
+#define HIFN_PUSTAT_REV_3 0x0030 /* 7751 PT6/3 */
+
+/* FIFO Status Register (HIFN_0_FIFOSTAT) */
+#define HIFN_FIFOSTAT_SRC 0x7f00 /* Source FIFO available */
+#define HIFN_FIFOSTAT_DST 0x007f /* Destination FIFO available */
+
+/* FIFO Configuration Register (HIFN_0_FIFOCNFG) */
+#define HIFN_FIFOCNFG_THRESHOLD 0x0400 /* must be written as 1 */
+
+/*
+ * DMA Interface Registers (offset from BASEREG1)
+ */
+#define HIFN_1_DMA_CRAR 0x0c /* DMA Command Ring Address */
+#define HIFN_1_DMA_SRAR 0x1c /* DMA Source Ring Address */
+#define HIFN_1_DMA_RRAR 0x2c /* DMA Result Ring Address */
+#define HIFN_1_DMA_DRAR 0x3c /* DMA Destination Ring Address */
+#define HIFN_1_DMA_CSR 0x40 /* DMA Status and Control */
+#define HIFN_1_DMA_IER 0x44 /* DMA Interrupt Enable */
+#define HIFN_1_DMA_CNFG 0x48 /* DMA Configuration */
+#define HIFN_1_PLL 0x4c /* 795x: PLL config */
+#define HIFN_1_7811_RNGENA 0x60 /* 7811: rng enable */
+#define HIFN_1_7811_RNGCFG 0x64 /* 7811: rng config */
+#define HIFN_1_7811_RNGDAT 0x68 /* 7811: rng data */
+#define HIFN_1_7811_RNGSTS 0x6c /* 7811: rng status */
+#define HIFN_1_7811_MIPSRST 0x94 /* 7811: MIPS reset */
+#define HIFN_1_REVID 0x98 /* Revision ID */
+#define HIFN_1_UNLOCK_SECRET1 0xf4
+#define HIFN_1_UNLOCK_SECRET2 0xfc
+#define HIFN_1_PUB_RESET 0x204 /* Public/RNG Reset */
+#define HIFN_1_PUB_BASE 0x300 /* Public Base Address */
+#define HIFN_1_PUB_OPLEN 0x304 /* Public Operand Length */
+#define HIFN_1_PUB_OP 0x308 /* Public Operand */
+#define HIFN_1_PUB_STATUS 0x30c /* Public Status */
+#define HIFN_1_PUB_IEN 0x310 /* Public Interrupt enable */
+#define HIFN_1_RNG_CONFIG 0x314 /* RNG config */
+#define HIFN_1_RNG_DATA 0x318 /* RNG data */
+#define HIFN_1_PUB_MEM 0x400 /* start of Public key memory */
+#define HIFN_1_PUB_MEMEND 0xbff /* end of Public key memory */
+
+/* DMA Status and Control Register (HIFN_1_DMA_CSR) */
+#define HIFN_DMACSR_D_CTRLMASK 0xc0000000 /* Destinition Ring Control */
+#define HIFN_DMACSR_D_CTRL_NOP 0x00000000 /* Dest. Control: no-op */
+#define HIFN_DMACSR_D_CTRL_DIS 0x40000000 /* Dest. Control: disable */
+#define HIFN_DMACSR_D_CTRL_ENA 0x80000000 /* Dest. Control: enable */
+#define HIFN_DMACSR_D_ABORT 0x20000000 /* Destinition Ring PCIAbort */
+#define HIFN_DMACSR_D_DONE 0x10000000 /* Destinition Ring Done */
+#define HIFN_DMACSR_D_LAST 0x08000000 /* Destinition Ring Last */
+#define HIFN_DMACSR_D_WAIT 0x04000000 /* Destinition Ring Waiting */
+#define HIFN_DMACSR_D_OVER 0x02000000 /* Destinition Ring Overflow */
+#define HIFN_DMACSR_R_CTRL 0x00c00000 /* Result Ring Control */
+#define HIFN_DMACSR_R_CTRL_NOP 0x00000000 /* Result Control: no-op */
+#define HIFN_DMACSR_R_CTRL_DIS 0x00400000 /* Result Control: disable */
+#define HIFN_DMACSR_R_CTRL_ENA 0x00800000 /* Result Control: enable */
+#define HIFN_DMACSR_R_ABORT 0x00200000 /* Result Ring PCI Abort */
+#define HIFN_DMACSR_R_DONE 0x00100000 /* Result Ring Done */
+#define HIFN_DMACSR_R_LAST 0x00080000 /* Result Ring Last */
+#define HIFN_DMACSR_R_WAIT 0x00040000 /* Result Ring Waiting */
+#define HIFN_DMACSR_R_OVER 0x00020000 /* Result Ring Overflow */
+#define HIFN_DMACSR_S_CTRL 0x0000c000 /* Source Ring Control */
+#define HIFN_DMACSR_S_CTRL_NOP 0x00000000 /* Source Control: no-op */
+#define HIFN_DMACSR_S_CTRL_DIS 0x00004000 /* Source Control: disable */
+#define HIFN_DMACSR_S_CTRL_ENA 0x00008000 /* Source Control: enable */
+#define HIFN_DMACSR_S_ABORT 0x00002000 /* Source Ring PCI Abort */
+#define HIFN_DMACSR_S_DONE 0x00001000 /* Source Ring Done */
+#define HIFN_DMACSR_S_LAST 0x00000800 /* Source Ring Last */
+#define HIFN_DMACSR_S_WAIT 0x00000400 /* Source Ring Waiting */
+#define HIFN_DMACSR_ILLW 0x00000200 /* Illegal write (7811 only) */
+#define HIFN_DMACSR_ILLR 0x00000100 /* Illegal read (7811 only) */
+#define HIFN_DMACSR_C_CTRL 0x000000c0 /* Command Ring Control */
+#define HIFN_DMACSR_C_CTRL_NOP 0x00000000 /* Command Control: no-op */
+#define HIFN_DMACSR_C_CTRL_DIS 0x00000040 /* Command Control: disable */
+#define HIFN_DMACSR_C_CTRL_ENA 0x00000080 /* Command Control: enable */
+#define HIFN_DMACSR_C_ABORT 0x00000020 /* Command Ring PCI Abort */
+#define HIFN_DMACSR_C_DONE 0x00000010 /* Command Ring Done */
+#define HIFN_DMACSR_C_LAST 0x00000008 /* Command Ring Last */
+#define HIFN_DMACSR_C_WAIT 0x00000004 /* Command Ring Waiting */
+#define HIFN_DMACSR_PUBDONE 0x00000002 /* Public op done (7951 only) */
+#define HIFN_DMACSR_ENGINE 0x00000001 /* Command Ring Engine IRQ */
+
+/* DMA Interrupt Enable Register (HIFN_1_DMA_IER) */
+#define HIFN_DMAIER_D_ABORT 0x20000000 /* Destination Ring PCIAbort */
+#define HIFN_DMAIER_D_DONE 0x10000000 /* Destination Ring Done */
+#define HIFN_DMAIER_D_LAST 0x08000000 /* Destination Ring Last */
+#define HIFN_DMAIER_D_WAIT 0x04000000 /* Destination Ring Waiting */
+#define HIFN_DMAIER_D_OVER 0x02000000 /* Destination Ring Overflow */
+#define HIFN_DMAIER_R_ABORT 0x00200000 /* Result Ring PCI Abort */
+#define HIFN_DMAIER_R_DONE 0x00100000 /* Result Ring Done */
+#define HIFN_DMAIER_R_LAST 0x00080000 /* Result Ring Last */
+#define HIFN_DMAIER_R_WAIT 0x00040000 /* Result Ring Waiting */
+#define HIFN_DMAIER_R_OVER 0x00020000 /* Result Ring Overflow */
+#define HIFN_DMAIER_S_ABORT 0x00002000 /* Source Ring PCI Abort */
+#define HIFN_DMAIER_S_DONE 0x00001000 /* Source Ring Done */
+#define HIFN_DMAIER_S_LAST 0x00000800 /* Source Ring Last */
+#define HIFN_DMAIER_S_WAIT 0x00000400 /* Source Ring Waiting */
+#define HIFN_DMAIER_ILLW 0x00000200 /* Illegal write (7811 only) */
+#define HIFN_DMAIER_ILLR 0x00000100 /* Illegal read (7811 only) */
+#define HIFN_DMAIER_C_ABORT 0x00000020 /* Command Ring PCI Abort */
+#define HIFN_DMAIER_C_DONE 0x00000010 /* Command Ring Done */
+#define HIFN_DMAIER_C_LAST 0x00000008 /* Command Ring Last */
+#define HIFN_DMAIER_C_WAIT 0x00000004 /* Command Ring Waiting */
+#define HIFN_DMAIER_PUBDONE 0x00000002 /* public op done (7951 only) */
+#define HIFN_DMAIER_ENGINE 0x00000001 /* Engine IRQ */
+
+/* DMA Configuration Register (HIFN_1_DMA_CNFG) */
+#define HIFN_DMACNFG_BIGENDIAN 0x10000000 /* big endian mode */
+#define HIFN_DMACNFG_POLLFREQ 0x00ff0000 /* Poll frequency mask */
+#define HIFN_DMACNFG_UNLOCK 0x00000800
+#define HIFN_DMACNFG_POLLINVAL 0x00000700 /* Invalid Poll Scalar */
+#define HIFN_DMACNFG_LAST 0x00000010 /* Host control LAST bit */
+#define HIFN_DMACNFG_MODE 0x00000004 /* DMA mode */
+#define HIFN_DMACNFG_DMARESET 0x00000002 /* DMA Reset # */
+#define HIFN_DMACNFG_MSTRESET 0x00000001 /* Master Reset # */
+
+/* PLL configuration register */
+#define HIFN_PLL_REF_CLK_HBI 0x00000000 /* HBI reference clock */
+#define HIFN_PLL_REF_CLK_PLL 0x00000001 /* PLL reference clock */
+#define HIFN_PLL_BP 0x00000002 /* Reference clock bypass */
+#define HIFN_PLL_PK_CLK_HBI 0x00000000 /* PK engine HBI clock */
+#define HIFN_PLL_PK_CLK_PLL 0x00000008 /* PK engine PLL clock */
+#define HIFN_PLL_PE_CLK_HBI 0x00000000 /* PE engine HBI clock */
+#define HIFN_PLL_PE_CLK_PLL 0x00000010 /* PE engine PLL clock */
+#define HIFN_PLL_RESERVED_1 0x00000400 /* Reserved bit, must be 1 */
+#define HIFN_PLL_ND_SHIFT 11 /* Clock multiplier shift */
+#define HIFN_PLL_ND_MULT_2 0x00000000 /* PLL clock multiplier 2 */
+#define HIFN_PLL_ND_MULT_4 0x00000800 /* PLL clock multiplier 4 */
+#define HIFN_PLL_ND_MULT_6 0x00001000 /* PLL clock multiplier 6 */
+#define HIFN_PLL_ND_MULT_8 0x00001800 /* PLL clock multiplier 8 */
+#define HIFN_PLL_ND_MULT_10 0x00002000 /* PLL clock multiplier 10 */
+#define HIFN_PLL_ND_MULT_12 0x00002800 /* PLL clock multiplier 12 */
+#define HIFN_PLL_IS_1_8 0x00000000 /* charge pump (mult. 1-8) */
+#define HIFN_PLL_IS_9_12 0x00010000 /* charge pump (mult. 9-12) */
+
+#define HIFN_PLL_FCK_MAX 266 /* Maximum PLL frequency */
+
+/* Public key reset register (HIFN_1_PUB_RESET) */
+#define HIFN_PUBRST_RESET 0x00000001 /* reset public/rng unit */
+
+/* Public base address register (HIFN_1_PUB_BASE) */
+#define HIFN_PUBBASE_ADDR 0x00003fff /* base address */
+
+/* Public operand length register (HIFN_1_PUB_OPLEN) */
+#define HIFN_PUBOPLEN_MOD_M 0x0000007f /* modulus length mask */
+#define HIFN_PUBOPLEN_MOD_S 0 /* modulus length shift */
+#define HIFN_PUBOPLEN_EXP_M 0x0003ff80 /* exponent length mask */
+#define HIFN_PUBOPLEN_EXP_S 7 /* exponent lenght shift */
+#define HIFN_PUBOPLEN_RED_M 0x003c0000 /* reducend length mask */
+#define HIFN_PUBOPLEN_RED_S 18 /* reducend length shift */
+
+/* Public operation register (HIFN_1_PUB_OP) */
+#define HIFN_PUBOP_AOFFSET_M 0x0000007f /* A offset mask */
+#define HIFN_PUBOP_AOFFSET_S 0 /* A offset shift */
+#define HIFN_PUBOP_BOFFSET_M 0x00000f80 /* B offset mask */
+#define HIFN_PUBOP_BOFFSET_S 7 /* B offset shift */
+#define HIFN_PUBOP_MOFFSET_M 0x0003f000 /* M offset mask */
+#define HIFN_PUBOP_MOFFSET_S 12 /* M offset shift */
+#define HIFN_PUBOP_OP_MASK 0x003c0000 /* Opcode: */
+#define HIFN_PUBOP_OP_NOP 0x00000000 /* NOP */
+#define HIFN_PUBOP_OP_ADD 0x00040000 /* ADD */
+#define HIFN_PUBOP_OP_ADDC 0x00080000 /* ADD w/carry */
+#define HIFN_PUBOP_OP_SUB 0x000c0000 /* SUB */
+#define HIFN_PUBOP_OP_SUBC 0x00100000 /* SUB w/carry */
+#define HIFN_PUBOP_OP_MODADD 0x00140000 /* Modular ADD */
+#define HIFN_PUBOP_OP_MODSUB 0x00180000 /* Modular SUB */
+#define HIFN_PUBOP_OP_INCA 0x001c0000 /* INC A */
+#define HIFN_PUBOP_OP_DECA 0x00200000 /* DEC A */
+#define HIFN_PUBOP_OP_MULT 0x00240000 /* MULT */
+#define HIFN_PUBOP_OP_MODMULT 0x00280000 /* Modular MULT */
+#define HIFN_PUBOP_OP_MODRED 0x002c0000 /* Modular RED */
+#define HIFN_PUBOP_OP_MODEXP 0x00300000 /* Modular EXP */
+
+/* Public status register (HIFN_1_PUB_STATUS) */
+#define HIFN_PUBSTS_DONE 0x00000001 /* operation done */
+#define HIFN_PUBSTS_CARRY 0x00000002 /* carry */
+
+/* Public interrupt enable register (HIFN_1_PUB_IEN) */
+#define HIFN_PUBIEN_DONE 0x00000001 /* operation done interrupt */
+
+/* Random number generator config register (HIFN_1_RNG_CONFIG) */
+#define HIFN_RNGCFG_ENA 0x00000001 /* enable rng */
+
+#define HIFN_NAMESIZE 32
+#define HIFN_MAX_RESULT_ORDER 5
+
+#define HIFN_D_CMD_RSIZE 24*4
+#define HIFN_D_SRC_RSIZE 80*4
+#define HIFN_D_DST_RSIZE 80*4
+#define HIFN_D_RES_RSIZE 24*4
+
+#define HIFN_QUEUE_LENGTH HIFN_D_CMD_RSIZE-5
+
+#define AES_MIN_KEY_SIZE 16
+#define AES_MAX_KEY_SIZE 32
+
+#define HIFN_DES_KEY_LENGTH 8
+#define HIFN_3DES_KEY_LENGTH 24
+#define HIFN_MAX_CRYPT_KEY_LENGTH AES_MAX_KEY_SIZE
+#define HIFN_IV_LENGTH 8
+#define HIFN_AES_IV_LENGTH 16
+#define HIFN_MAX_IV_LENGTH HIFN_AES_IV_LENGTH
+
+#define HIFN_MAC_KEY_LENGTH 64
+#define HIFN_MD5_LENGTH 16
+#define HIFN_SHA1_LENGTH 20
+#define HIFN_MAC_TRUNC_LENGTH 12
+
+#define HIFN_MAX_COMMAND (8 + 8 + 8 + 64 + 260)
+#define HIFN_MAX_RESULT (8 + 4 + 4 + 20 + 4)
+#define HIFN_USED_RESULT 12
+
+struct hifn_desc
+{
+ volatile u32 l;
+ volatile u32 p;
+};
+
+struct hifn_dma {
+ struct hifn_desc cmdr[HIFN_D_CMD_RSIZE+1];
+ struct hifn_desc srcr[HIFN_D_SRC_RSIZE+1];
+ struct hifn_desc dstr[HIFN_D_DST_RSIZE+1];
+ struct hifn_desc resr[HIFN_D_RES_RSIZE+1];
+
+ u8 command_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_COMMAND];
+ u8 result_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_RESULT];
+
+ u64 test_src, test_dst;
+
+ /*
+ * Our current positions for insertion and removal from the descriptor
+ * rings.
+ */
+ volatile int cmdi, srci, dsti, resi;
+ volatile int cmdu, srcu, dstu, resu;
+ int cmdk, srck, dstk, resk;
+};
+
+#define HIFN_FLAG_CMD_BUSY (1<<0)
+#define HIFN_FLAG_SRC_BUSY (1<<1)
+#define HIFN_FLAG_DST_BUSY (1<<2)
+#define HIFN_FLAG_RES_BUSY (1<<3)
+#define HIFN_FLAG_OLD_KEY (1<<4)
+
+#define HIFN_DEFAULT_ACTIVE_NUM 5
+
+struct hifn_device
+{
+ char name[HIFN_NAMESIZE];
+
+ int irq;
+
+ struct pci_dev *pdev;
+ void __iomem *bar[3];
+
+ unsigned long result_mem;
+ dma_addr_t dst;
+
+ void *desc_virt;
+ dma_addr_t desc_dma;
+
+ u32 dmareg;
+
+ void *sa[HIFN_D_RES_RSIZE];
+
+ spinlock_t lock;
+
+ void *priv;
+
+ u32 flags;
+ int active, started;
+ struct delayed_work work;
+ unsigned long reset;
+ unsigned long success;
+ unsigned long prev_success;
+
+ u8 snum;
+
+ struct tasklet_struct tasklet;
+
+ struct crypto_queue queue;
+ struct list_head alg_list;
+
+ unsigned int pk_clk_freq;
+
+#if defined(CONFIG_HW_RANDOM) || defined(CONFIG_HW_RANDOM_MODULE)
+ unsigned int rng_wait_time;
+ ktime_t rngtime;
+ struct hwrng rng;
+#endif
+};
+
+#define HIFN_D_LENGTH 0x0000ffff
+#define HIFN_D_NOINVALID 0x01000000
+#define HIFN_D_MASKDONEIRQ 0x02000000
+#define HIFN_D_DESTOVER 0x04000000
+#define HIFN_D_OVER 0x08000000
+#define HIFN_D_LAST 0x20000000
+#define HIFN_D_JUMP 0x40000000
+#define HIFN_D_VALID 0x80000000
+
+struct hifn_base_command
+{
+ volatile u16 masks;
+ volatile u16 session_num;
+ volatile u16 total_source_count;
+ volatile u16 total_dest_count;
+};
+
+#define HIFN_BASE_CMD_COMP 0x0100 /* enable compression engine */
+#define HIFN_BASE_CMD_PAD 0x0200 /* enable padding engine */
+#define HIFN_BASE_CMD_MAC 0x0400 /* enable MAC engine */
+#define HIFN_BASE_CMD_CRYPT 0x0800 /* enable crypt engine */
+#define HIFN_BASE_CMD_DECODE 0x2000
+#define HIFN_BASE_CMD_SRCLEN_M 0xc000
+#define HIFN_BASE_CMD_SRCLEN_S 14
+#define HIFN_BASE_CMD_DSTLEN_M 0x3000
+#define HIFN_BASE_CMD_DSTLEN_S 12
+#define HIFN_BASE_CMD_LENMASK_HI 0x30000
+#define HIFN_BASE_CMD_LENMASK_LO 0x0ffff
+
+/*
+ * Structure to help build up the command data structure.
+ */
+struct hifn_crypt_command
+{
+ volatile u16 masks;
+ volatile u16 header_skip;
+ volatile u16 source_count;
+ volatile u16 reserved;
+};
+
+#define HIFN_CRYPT_CMD_ALG_MASK 0x0003 /* algorithm: */
+#define HIFN_CRYPT_CMD_ALG_DES 0x0000 /* DES */
+#define HIFN_CRYPT_CMD_ALG_3DES 0x0001 /* 3DES */
+#define HIFN_CRYPT_CMD_ALG_RC4 0x0002 /* RC4 */
+#define HIFN_CRYPT_CMD_ALG_AES 0x0003 /* AES */
+#define HIFN_CRYPT_CMD_MODE_MASK 0x0018 /* Encrypt mode: */
+#define HIFN_CRYPT_CMD_MODE_ECB 0x0000 /* ECB */
+#define HIFN_CRYPT_CMD_MODE_CBC 0x0008 /* CBC */
+#define HIFN_CRYPT_CMD_MODE_CFB 0x0010 /* CFB */
+#define HIFN_CRYPT_CMD_MODE_OFB 0x0018 /* OFB */
+#define HIFN_CRYPT_CMD_CLR_CTX 0x0040 /* clear context */
+#define HIFN_CRYPT_CMD_KSZ_MASK 0x0600 /* AES key size: */
+#define HIFN_CRYPT_CMD_KSZ_128 0x0000 /* 128 bit */
+#define HIFN_CRYPT_CMD_KSZ_192 0x0200 /* 192 bit */
+#define HIFN_CRYPT_CMD_KSZ_256 0x0400 /* 256 bit */
+#define HIFN_CRYPT_CMD_NEW_KEY 0x0800 /* expect new key */
+#define HIFN_CRYPT_CMD_NEW_IV 0x1000 /* expect new iv */
+#define HIFN_CRYPT_CMD_SRCLEN_M 0xc000
+#define HIFN_CRYPT_CMD_SRCLEN_S 14
+
+/*
+ * Structure to help build up the command data structure.
+ */
+struct hifn_mac_command
+{
+ volatile u16 masks;
+ volatile u16 header_skip;
+ volatile u16 source_count;
+ volatile u16 reserved;
+};
+
+#define HIFN_MAC_CMD_ALG_MASK 0x0001
+#define HIFN_MAC_CMD_ALG_SHA1 0x0000
+#define HIFN_MAC_CMD_ALG_MD5 0x0001
+#define HIFN_MAC_CMD_MODE_MASK 0x000c
+#define HIFN_MAC_CMD_MODE_HMAC 0x0000
+#define HIFN_MAC_CMD_MODE_SSL_MAC 0x0004
+#define HIFN_MAC_CMD_MODE_HASH 0x0008
+#define HIFN_MAC_CMD_MODE_FULL 0x0004
+#define HIFN_MAC_CMD_TRUNC 0x0010
+#define HIFN_MAC_CMD_RESULT 0x0020
+#define HIFN_MAC_CMD_APPEND 0x0040
+#define HIFN_MAC_CMD_SRCLEN_M 0xc000
+#define HIFN_MAC_CMD_SRCLEN_S 14
+
+/*
+ * MAC POS IPsec initiates authentication after encryption on encodes
+ * and before decryption on decodes.
+ */
+#define HIFN_MAC_CMD_POS_IPSEC 0x0200
+#define HIFN_MAC_CMD_NEW_KEY 0x0800
+
+struct hifn_comp_command
+{
+ volatile u16 masks;
+ volatile u16 header_skip;
+ volatile u16 source_count;
+ volatile u16 reserved;
+};
+
+#define HIFN_COMP_CMD_SRCLEN_M 0xc000
+#define HIFN_COMP_CMD_SRCLEN_S 14
+#define HIFN_COMP_CMD_ONE 0x0100 /* must be one */
+#define HIFN_COMP_CMD_CLEARHIST 0x0010 /* clear history */
+#define HIFN_COMP_CMD_UPDATEHIST 0x0008 /* update history */
+#define HIFN_COMP_CMD_LZS_STRIP0 0x0004 /* LZS: strip zero */
+#define HIFN_COMP_CMD_MPPC_RESTART 0x0004 /* MPPC: restart */
+#define HIFN_COMP_CMD_ALG_MASK 0x0001 /* compression mode: */
+#define HIFN_COMP_CMD_ALG_MPPC 0x0001 /* MPPC */
+#define HIFN_COMP_CMD_ALG_LZS 0x0000 /* LZS */
+
+struct hifn_base_result
+{
+ volatile u16 flags;
+ volatile u16 session;
+ volatile u16 src_cnt; /* 15:0 of source count */
+ volatile u16 dst_cnt; /* 15:0 of dest count */
+};
+
+#define HIFN_BASE_RES_DSTOVERRUN 0x0200 /* destination overrun */
+#define HIFN_BASE_RES_SRCLEN_M 0xc000 /* 17:16 of source count */
+#define HIFN_BASE_RES_SRCLEN_S 14
+#define HIFN_BASE_RES_DSTLEN_M 0x3000 /* 17:16 of dest count */
+#define HIFN_BASE_RES_DSTLEN_S 12
+
+struct hifn_comp_result
+{
+ volatile u16 flags;
+ volatile u16 crc;
+};
+
+#define HIFN_COMP_RES_LCB_M 0xff00 /* longitudinal check byte */
+#define HIFN_COMP_RES_LCB_S 8
+#define HIFN_COMP_RES_RESTART 0x0004 /* MPPC: restart */
+#define HIFN_COMP_RES_ENDMARKER 0x0002 /* LZS: end marker seen */
+#define HIFN_COMP_RES_SRC_NOTZERO 0x0001 /* source expired */
+
+struct hifn_mac_result
+{
+ volatile u16 flags;
+ volatile u16 reserved;
+ /* followed by 0, 6, 8, or 10 u16's of the MAC, then crypt */
+};
+
+#define HIFN_MAC_RES_MISCOMPARE 0x0002 /* compare failed */
+#define HIFN_MAC_RES_SRC_NOTZERO 0x0001 /* source expired */
+
+struct hifn_crypt_result
+{
+ volatile u16 flags;
+ volatile u16 reserved;
+};
+
+#define HIFN_CRYPT_RES_SRC_NOTZERO 0x0001 /* source expired */
+
+#ifndef HIFN_POLL_FREQUENCY
+#define HIFN_POLL_FREQUENCY 0x1
+#endif
+
+#ifndef HIFN_POLL_SCALAR
+#define HIFN_POLL_SCALAR 0x0
+#endif
+
+#define HIFN_MAX_SEGLEN 0xffff /* maximum dma segment len */
+#define HIFN_MAX_DMALEN 0x3ffff /* maximum dma length */
+
+struct hifn_crypto_alg
+{
+ struct list_head entry;
+ struct crypto_alg alg;
+ struct hifn_device *dev;
+};
+
+#define ASYNC_SCATTERLIST_CACHE 16
+
+#define ASYNC_FLAGS_MISALIGNED (1<<0)
+
+struct ablkcipher_walk
+{
+ struct scatterlist cache[ASYNC_SCATTERLIST_CACHE];
+ u32 flags;
+ int num;
+};
+
+struct hifn_context
+{
+ u8 key[HIFN_MAX_CRYPT_KEY_LENGTH], *iv;
+ struct hifn_device *dev;
+ unsigned int keysize, ivsize;
+ u8 op, type, mode, unused;
+ struct ablkcipher_walk walk;
+ atomic_t sg_num;
+};
+
+#define crypto_alg_to_hifn(a) container_of(a, struct hifn_crypto_alg, alg)
+
+static inline u32 hifn_read_0(struct hifn_device *dev, u32 reg)
+{
+ u32 ret;
+
+ ret = readl((char *)(dev->bar[0]) + reg);
+
+ return ret;
+}
+
+static inline u32 hifn_read_1(struct hifn_device *dev, u32 reg)
+{
+ u32 ret;
+
+ ret = readl((char *)(dev->bar[1]) + reg);
+
+ return ret;
+}
+
+static inline void hifn_write_0(struct hifn_device *dev, u32 reg, u32 val)
+{
+ writel(val, (char *)(dev->bar[0]) + reg);
+}
+
+static inline void hifn_write_1(struct hifn_device *dev, u32 reg, u32 val)
+{
+ writel(val, (char *)(dev->bar[1]) + reg);
+}
+
+static void hifn_wait_puc(struct hifn_device *dev)
+{
+ int i;
+ u32 ret;
+
+ for (i=10000; i > 0; --i) {
+ ret = hifn_read_0(dev, HIFN_0_PUCTRL);
+ if (!(ret & HIFN_PUCTRL_RESET))
+ break;
+
+ udelay(1);
+ }
+
+ if (!i)
+ dprintk("%s: Failed to reset PUC unit.\n", dev->name);
+}
+
+static void hifn_reset_puc(struct hifn_device *dev)
+{
+ hifn_write_0(dev, HIFN_0_PUCTRL, HIFN_PUCTRL_DMAENA);
+ hifn_wait_puc(dev);
+}
+
+static void hifn_stop_device(struct hifn_device *dev)
+{
+ hifn_write_1(dev, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS |
+ HIFN_DMACSR_S_CTRL_DIS | HIFN_DMACSR_C_CTRL_DIS);
+ hifn_write_0(dev, HIFN_0_PUIER, 0);
+ hifn_write_1(dev, HIFN_1_DMA_IER, 0);
+}
+
+static void hifn_reset_dma(struct hifn_device *dev, int full)
+{
+ hifn_stop_device(dev);
+
+ /*
+ * Setting poll frequency and others to 0.
+ */
+ hifn_write_1(dev, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+ mdelay(1);
+
+ /*
+ * Reset DMA.
+ */
+ if (full) {
+ hifn_write_1(dev, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MODE);
+ mdelay(1);
+ } else {
+ hifn_write_1(dev, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MODE |
+ HIFN_DMACNFG_MSTRESET);
+ hifn_reset_puc(dev);
+ }
+
+ hifn_write_1(dev, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+
+ hifn_reset_puc(dev);
+}
+
+static u32 hifn_next_signature(u_int32_t a, u_int cnt)
+{
+ int i;
+ u32 v;
+
+ for (i = 0; i < cnt; i++) {
+
+ /* get the parity */
+ v = a & 0x80080125;
+ v ^= v >> 16;
+ v ^= v >> 8;
+ v ^= v >> 4;
+ v ^= v >> 2;
+ v ^= v >> 1;
+
+ a = (v & 1) ^ (a << 1);
+ }
+
+ return a;
+}
+
+static struct pci2id {
+ u_short pci_vendor;
+ u_short pci_prod;
+ char card_id[13];
+} pci2id[] = {
+ {
+ PCI_VENDOR_ID_HIFN,
+ PCI_DEVICE_ID_HIFN_7955,
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00 }
+ },
+ {
+ PCI_VENDOR_ID_HIFN,
+ PCI_DEVICE_ID_HIFN_7956,
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00 }
+ }
+};
+
+#if defined(CONFIG_HW_RANDOM) || defined(CONFIG_HW_RANDOM_MODULE)
+static int hifn_rng_data_present(struct hwrng *rng, int wait)
+{
+ struct hifn_device *dev = (struct hifn_device *)rng->priv;
+ s64 nsec;
+
+ nsec = ktime_to_ns(ktime_sub(ktime_get(), dev->rngtime));
+ nsec -= dev->rng_wait_time;
+ if (nsec <= 0)
+ return 1;
+ if (!wait)
+ return 0;
+ ndelay(nsec);
+ return 1;
+}
+
+static int hifn_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ struct hifn_device *dev = (struct hifn_device *)rng->priv;
+
+ *data = hifn_read_1(dev, HIFN_1_RNG_DATA);
+ dev->rngtime = ktime_get();
+ return 4;
+}
+
+static int hifn_register_rng(struct hifn_device *dev)
+{
+ /*
+ * We must wait at least 256 Pk_clk cycles between two reads of the rng.
+ */
+ dev->rng_wait_time = DIV_ROUND_UP(NSEC_PER_SEC, dev->pk_clk_freq) *
+ 256;
+
+ dev->rng.name = dev->name;
+ dev->rng.data_present = hifn_rng_data_present,
+ dev->rng.data_read = hifn_rng_data_read,
+ dev->rng.priv = (unsigned long)dev;
+
+ return hwrng_register(&dev->rng);
+}
+
+static void hifn_unregister_rng(struct hifn_device *dev)
+{
+ hwrng_unregister(&dev->rng);
+}
+#else
+#define hifn_register_rng(dev) 0
+#define hifn_unregister_rng(dev)
+#endif
+
+static int hifn_init_pubrng(struct hifn_device *dev)
+{
+ int i;
+
+ hifn_write_1(dev, HIFN_1_PUB_RESET, hifn_read_1(dev, HIFN_1_PUB_RESET) |
+ HIFN_PUBRST_RESET);
+
+ for (i=100; i > 0; --i) {
+ mdelay(1);
+
+ if ((hifn_read_1(dev, HIFN_1_PUB_RESET) & HIFN_PUBRST_RESET) == 0)
+ break;
+ }
+
+ if (!i)
+ dprintk("Chip %s: Failed to initialise public key engine.\n",
+ dev->name);
+ else {
+ hifn_write_1(dev, HIFN_1_PUB_IEN, HIFN_PUBIEN_DONE);
+ dev->dmareg |= HIFN_DMAIER_PUBDONE;
+ hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
+
+ dprintk("Chip %s: Public key engine has been sucessfully "
+ "initialised.\n", dev->name);
+ }
+
+ /*
+ * Enable RNG engine.
+ */
+
+ hifn_write_1(dev, HIFN_1_RNG_CONFIG,
+ hifn_read_1(dev, HIFN_1_RNG_CONFIG) | HIFN_RNGCFG_ENA);
+ dprintk("Chip %s: RNG engine has been successfully initialised.\n",
+ dev->name);
+
+#if defined(CONFIG_HW_RANDOM) || defined(CONFIG_HW_RANDOM_MODULE)
+ /* First value must be discarded */
+ hifn_read_1(dev, HIFN_1_RNG_DATA);
+ dev->rngtime = ktime_get();
+#endif
+ return 0;
+}
+
+static int hifn_enable_crypto(struct hifn_device *dev)
+{
+ u32 dmacfg, addr;
+ char *offtbl = NULL;
+ int i;
+
+ for (i = 0; i < sizeof(pci2id)/sizeof(pci2id[0]); i++) {
+ if (pci2id[i].pci_vendor == dev->pdev->vendor &&
+ pci2id[i].pci_prod == dev->pdev->device) {
+ offtbl = pci2id[i].card_id;
+ break;
+ }
+ }
+
+ if (offtbl == NULL) {
+ dprintk("Chip %s: Unknown card!\n", dev->name);
+ return -ENODEV;
+ }
+
+ dmacfg = hifn_read_1(dev, HIFN_1_DMA_CNFG);
+
+ hifn_write_1(dev, HIFN_1_DMA_CNFG,
+ HIFN_DMACNFG_UNLOCK | HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+ mdelay(1);
+ addr = hifn_read_1(dev, HIFN_1_UNLOCK_SECRET1);
+ mdelay(1);
+ hifn_write_1(dev, HIFN_1_UNLOCK_SECRET2, 0);
+ mdelay(1);
+
+ for (i=0; i<12; ++i) {
+ addr = hifn_next_signature(addr, offtbl[i] + 0x101);
+ hifn_write_1(dev, HIFN_1_UNLOCK_SECRET2, addr);
+
+ mdelay(1);
+ }
+ hifn_write_1(dev, HIFN_1_DMA_CNFG, dmacfg);
+
+ dprintk("Chip %s: %s.\n", dev->name, pci_name(dev->pdev));
+
+ return 0;
+}
+
+static void hifn_init_dma(struct hifn_device *dev)
+{
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ u32 dptr = dev->desc_dma;
+ int i;
+
+ for (i=0; i<HIFN_D_CMD_RSIZE; ++i)
+ dma->cmdr[i].p = __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, command_bufs[i][0]));
+ for (i=0; i<HIFN_D_RES_RSIZE; ++i)
+ dma->resr[i].p = __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, result_bufs[i][0]));
+
+ /*
+ * Setup LAST descriptors.
+ */
+ dma->cmdr[HIFN_D_CMD_RSIZE].p = __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, cmdr[0]));
+ dma->srcr[HIFN_D_SRC_RSIZE].p = __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, srcr[0]));
+ dma->dstr[HIFN_D_DST_RSIZE].p = __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, dstr[0]));
+ dma->resr[HIFN_D_RES_RSIZE].p = __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, resr[0]));
+
+ dma->cmdu = dma->srcu = dma->dstu = dma->resu = 0;
+ dma->cmdi = dma->srci = dma->dsti = dma->resi = 0;
+ dma->cmdk = dma->srck = dma->dstk = dma->resk = 0;
+}
+
+/*
+ * Initialize the PLL. We need to know the frequency of the reference clock
+ * to calculate the optimal multiplier. For PCI we assume 66MHz, since that
+ * allows us to operate without the risk of overclocking the chip. If it
+ * actually uses 33MHz, the chip will operate at half the speed, this can be
+ * overriden by specifying the frequency as module parameter (pci33).
+ *
+ * Unfortunately the PCI clock is not very suitable since the HIFN needs a
+ * stable clock and the PCI clock frequency may vary, so the default is the
+ * external clock. There is no way to find out its frequency, we default to
+ * 66MHz since according to Mike Ham of HiFn, almost every board in existence
+ * has an external crystal populated at 66MHz.
+ */
+static void hifn_init_pll(struct hifn_device *dev)
+{
+ unsigned int freq, m;
+ u32 pllcfg;
+
+ pllcfg = HIFN_1_PLL | HIFN_PLL_RESERVED_1;
+
+ if (strncmp(hifn_pll_ref, "ext", 3) == 0)
+ pllcfg |= HIFN_PLL_REF_CLK_PLL;
+ else
+ pllcfg |= HIFN_PLL_REF_CLK_HBI;
+
+ if (hifn_pll_ref[3] != '\0')
+ freq = simple_strtoul(hifn_pll_ref + 3, NULL, 10);
+ else {
+ freq = 66;
+ printk(KERN_INFO "hifn795x: assuming %uMHz clock speed, "
+ "override with hifn_pll_ref=%.3s<frequency>\n",
+ freq, hifn_pll_ref);
+ }
+
+ m = HIFN_PLL_FCK_MAX / freq;
+
+ pllcfg |= (m / 2 - 1) << HIFN_PLL_ND_SHIFT;
+ if (m <= 8)
+ pllcfg |= HIFN_PLL_IS_1_8;
+ else
+ pllcfg |= HIFN_PLL_IS_9_12;
+
+ /* Select clock source and enable clock bypass */
+ hifn_write_1(dev, HIFN_1_PLL, pllcfg |
+ HIFN_PLL_PK_CLK_HBI | HIFN_PLL_PE_CLK_HBI | HIFN_PLL_BP);
+
+ /* Let the chip lock to the input clock */
+ mdelay(10);
+
+ /* Disable clock bypass */
+ hifn_write_1(dev, HIFN_1_PLL, pllcfg |
+ HIFN_PLL_PK_CLK_HBI | HIFN_PLL_PE_CLK_HBI);
+
+ /* Switch the engines to the PLL */
+ hifn_write_1(dev, HIFN_1_PLL, pllcfg |
+ HIFN_PLL_PK_CLK_PLL | HIFN_PLL_PE_CLK_PLL);
+
+ /*
+ * The Fpk_clk runs at half the total speed. Its frequency is needed to
+ * calculate the minimum time between two reads of the rng. Since 33MHz
+ * is actually 33.333... we overestimate the frequency here, resulting
+ * in slightly larger intervals.
+ */
+ dev->pk_clk_freq = 1000000 * (freq + 1) * m / 2;
+}
+
+static void hifn_init_registers(struct hifn_device *dev)
+{
+ u32 dptr = dev->desc_dma;
+
+ /* Initialization magic... */
+ hifn_write_0(dev, HIFN_0_PUCTRL, HIFN_PUCTRL_DMAENA);
+ hifn_write_0(dev, HIFN_0_FIFOCNFG, HIFN_FIFOCNFG_THRESHOLD);
+ hifn_write_0(dev, HIFN_0_PUIER, HIFN_PUIER_DSTOVER);
+
+ /* write all 4 ring address registers */
+ hifn_write_1(dev, HIFN_1_DMA_CRAR, __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, cmdr[0])));
+ hifn_write_1(dev, HIFN_1_DMA_SRAR, __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, srcr[0])));
+ hifn_write_1(dev, HIFN_1_DMA_DRAR, __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, dstr[0])));
+ hifn_write_1(dev, HIFN_1_DMA_RRAR, __cpu_to_le32(dptr +
+ offsetof(struct hifn_dma, resr[0])));
+
+ mdelay(2);
+#if 0
+ hifn_write_1(dev, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS |
+ HIFN_DMACSR_S_CTRL_DIS | HIFN_DMACSR_C_CTRL_DIS |
+ HIFN_DMACSR_D_ABORT | HIFN_DMACSR_D_DONE | HIFN_DMACSR_D_LAST |
+ HIFN_DMACSR_D_WAIT | HIFN_DMACSR_D_OVER |
+ HIFN_DMACSR_R_ABORT | HIFN_DMACSR_R_DONE | HIFN_DMACSR_R_LAST |
+ HIFN_DMACSR_R_WAIT | HIFN_DMACSR_R_OVER |
+ HIFN_DMACSR_S_ABORT | HIFN_DMACSR_S_DONE | HIFN_DMACSR_S_LAST |
+ HIFN_DMACSR_S_WAIT |
+ HIFN_DMACSR_C_ABORT | HIFN_DMACSR_C_DONE | HIFN_DMACSR_C_LAST |
+ HIFN_DMACSR_C_WAIT |
+ HIFN_DMACSR_ENGINE |
+ HIFN_DMACSR_PUBDONE);
+#else
+ hifn_write_1(dev, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_C_CTRL_ENA | HIFN_DMACSR_S_CTRL_ENA |
+ HIFN_DMACSR_D_CTRL_ENA | HIFN_DMACSR_R_CTRL_ENA |
+ HIFN_DMACSR_D_ABORT | HIFN_DMACSR_D_DONE | HIFN_DMACSR_D_LAST |
+ HIFN_DMACSR_D_WAIT | HIFN_DMACSR_D_OVER |
+ HIFN_DMACSR_R_ABORT | HIFN_DMACSR_R_DONE | HIFN_DMACSR_R_LAST |
+ HIFN_DMACSR_R_WAIT | HIFN_DMACSR_R_OVER |
+ HIFN_DMACSR_S_ABORT | HIFN_DMACSR_S_DONE | HIFN_DMACSR_S_LAST |
+ HIFN_DMACSR_S_WAIT |
+ HIFN_DMACSR_C_ABORT | HIFN_DMACSR_C_DONE | HIFN_DMACSR_C_LAST |
+ HIFN_DMACSR_C_WAIT |
+ HIFN_DMACSR_ENGINE |
+ HIFN_DMACSR_PUBDONE);
+#endif
+ hifn_read_1(dev, HIFN_1_DMA_CSR);
+
+ dev->dmareg |= HIFN_DMAIER_R_DONE | HIFN_DMAIER_C_ABORT |
+ HIFN_DMAIER_D_OVER | HIFN_DMAIER_R_OVER |
+ HIFN_DMAIER_S_ABORT | HIFN_DMAIER_D_ABORT | HIFN_DMAIER_R_ABORT |
+ HIFN_DMAIER_ENGINE;
+ dev->dmareg &= ~HIFN_DMAIER_C_WAIT;
+
+ hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
+ hifn_read_1(dev, HIFN_1_DMA_IER);
+#if 0
+ hifn_write_0(dev, HIFN_0_PUCNFG, HIFN_PUCNFG_ENCCNFG |
+ HIFN_PUCNFG_DRFR_128 | HIFN_PUCNFG_TCALLPHASES |
+ HIFN_PUCNFG_TCDRVTOTEM | HIFN_PUCNFG_BUS32 |
+ HIFN_PUCNFG_DRAM);
+#else
+ hifn_write_0(dev, HIFN_0_PUCNFG, 0x10342);
+#endif
+ hifn_init_pll(dev);
+
+ hifn_write_0(dev, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
+ hifn_write_1(dev, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE | HIFN_DMACNFG_LAST |
+ ((HIFN_POLL_FREQUENCY << 16 ) & HIFN_DMACNFG_POLLFREQ) |
+ ((HIFN_POLL_SCALAR << 8) & HIFN_DMACNFG_POLLINVAL));
+}
+
+static int hifn_setup_base_command(struct hifn_device *dev, u8 *buf,
+ unsigned dlen, unsigned slen, u16 mask, u8 snum)
+{
+ struct hifn_base_command *base_cmd;
+ u8 *buf_pos = buf;
+
+ base_cmd = (struct hifn_base_command *)buf_pos;
+ base_cmd->masks = __cpu_to_le16(mask);
+ base_cmd->total_source_count =
+ __cpu_to_le16(slen & HIFN_BASE_CMD_LENMASK_LO);
+ base_cmd->total_dest_count =
+ __cpu_to_le16(dlen & HIFN_BASE_CMD_LENMASK_LO);
+
+ dlen >>= 16;
+ slen >>= 16;
+ base_cmd->session_num = __cpu_to_le16(snum |
+ ((slen << HIFN_BASE_CMD_SRCLEN_S) & HIFN_BASE_CMD_SRCLEN_M) |
+ ((dlen << HIFN_BASE_CMD_DSTLEN_S) & HIFN_BASE_CMD_DSTLEN_M));
+
+ return sizeof(struct hifn_base_command);
+}
+
+static int hifn_setup_crypto_command(struct hifn_device *dev,
+ u8 *buf, unsigned dlen, unsigned slen,
+ u8 *key, int keylen, u8 *iv, int ivsize, u16 mode)
+{
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ struct hifn_crypt_command *cry_cmd;
+ u8 *buf_pos = buf;
+ u16 cmd_len;
+
+ cry_cmd = (struct hifn_crypt_command *)buf_pos;
+
+ cry_cmd->source_count = __cpu_to_le16(dlen & 0xffff);
+ dlen >>= 16;
+ cry_cmd->masks = __cpu_to_le16(mode |
+ ((dlen << HIFN_CRYPT_CMD_SRCLEN_S) &
+ HIFN_CRYPT_CMD_SRCLEN_M));
+ cry_cmd->header_skip = 0;
+ cry_cmd->reserved = 0;
+
+ buf_pos += sizeof(struct hifn_crypt_command);
+
+ dma->cmdu++;
+ if (dma->cmdu > 1) {
+ dev->dmareg |= HIFN_DMAIER_C_WAIT;
+ hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
+ }
+
+ if (keylen) {
+ memcpy(buf_pos, key, keylen);
+ buf_pos += keylen;
+ }
+ if (ivsize) {
+ memcpy(buf_pos, iv, ivsize);
+ buf_pos += ivsize;
+ }
+
+ cmd_len = buf_pos - buf;
+
+ return cmd_len;
+}
+
+static int hifn_setup_src_desc(struct hifn_device *dev, struct page *page,
+ unsigned int offset, unsigned int size)
+{
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ int idx;
+ dma_addr_t addr;
+
+ addr = pci_map_page(dev->pdev, page, offset, size, PCI_DMA_TODEVICE);
+
+ idx = dma->srci;
+
+ dma->srcr[idx].p = __cpu_to_le32(addr);
+ dma->srcr[idx].l = __cpu_to_le32(size) | HIFN_D_VALID |
+ HIFN_D_MASKDONEIRQ | HIFN_D_NOINVALID | HIFN_D_LAST;
+
+ if (++idx == HIFN_D_SRC_RSIZE) {
+ dma->srcr[idx].l = __cpu_to_le32(HIFN_D_VALID |
+ HIFN_D_JUMP |
+ HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+ idx = 0;
+ }
+
+ dma->srci = idx;
+ dma->srcu++;
+
+ if (!(dev->flags & HIFN_FLAG_SRC_BUSY)) {
+ hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_S_CTRL_ENA);
+ dev->flags |= HIFN_FLAG_SRC_BUSY;
+ }
+
+ return size;
+}
+
+static void hifn_setup_res_desc(struct hifn_device *dev)
+{
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+
+ dma->resr[dma->resi].l = __cpu_to_le32(HIFN_USED_RESULT |
+ HIFN_D_VALID | HIFN_D_LAST);
+ /*
+ * dma->resr[dma->resi].l = __cpu_to_le32(HIFN_MAX_RESULT | HIFN_D_VALID |
+ * HIFN_D_LAST | HIFN_D_NOINVALID);
+ */
+
+ if (++dma->resi == HIFN_D_RES_RSIZE) {
+ dma->resr[HIFN_D_RES_RSIZE].l = __cpu_to_le32(HIFN_D_VALID |
+ HIFN_D_JUMP | HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+ dma->resi = 0;
+ }
+
+ dma->resu++;
+
+ if (!(dev->flags & HIFN_FLAG_RES_BUSY)) {
+ hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_R_CTRL_ENA);
+ dev->flags |= HIFN_FLAG_RES_BUSY;
+ }
+}
+
+static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
+ unsigned offset, unsigned size)
+{
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ int idx;
+ dma_addr_t addr;
+
+ addr = pci_map_page(dev->pdev, page, offset, size, PCI_DMA_FROMDEVICE);
+
+ idx = dma->dsti;
+ dma->dstr[idx].p = __cpu_to_le32(addr);
+ dma->dstr[idx].l = __cpu_to_le32(size | HIFN_D_VALID |
+ HIFN_D_MASKDONEIRQ | HIFN_D_NOINVALID | HIFN_D_LAST);
+
+ if (++idx == HIFN_D_DST_RSIZE) {
+ dma->dstr[idx].l = __cpu_to_le32(HIFN_D_VALID |
+ HIFN_D_JUMP | HIFN_D_MASKDONEIRQ |
+ HIFN_D_LAST | HIFN_D_NOINVALID);
+ idx = 0;
+ }
+ dma->dsti = idx;
+ dma->dstu++;
+
+ if (!(dev->flags & HIFN_FLAG_DST_BUSY)) {
+ hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_D_CTRL_ENA);
+ dev->flags |= HIFN_FLAG_DST_BUSY;
+ }
+}
+
+static int hifn_setup_dma(struct hifn_device *dev, struct page *spage, unsigned int soff,
+ struct page *dpage, unsigned int doff, unsigned int nbytes, void *priv,
+ struct hifn_context *ctx)
+{
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ int cmd_len, sa_idx;
+ u8 *buf, *buf_pos;
+ u16 mask;
+
+ dprintk("%s: spage: %p, soffset: %u, dpage: %p, doffset: %u, nbytes: %u, priv: %p, ctx: %p.\n",
+ dev->name, spage, soff, dpage, doff, nbytes, priv, ctx);
+
+ sa_idx = dma->resi;
+
+ hifn_setup_src_desc(dev, spage, soff, nbytes);
+
+ buf_pos = buf = dma->command_bufs[dma->cmdi];
+
+ mask = 0;
+ switch (ctx->op) {
+ case ACRYPTO_OP_DECRYPT:
+ mask = HIFN_BASE_CMD_CRYPT | HIFN_BASE_CMD_DECODE;
+ break;
+ case ACRYPTO_OP_ENCRYPT:
+ mask = HIFN_BASE_CMD_CRYPT;
+ break;
+ case ACRYPTO_OP_HMAC:
+ mask = HIFN_BASE_CMD_MAC;
+ break;
+ default:
+ goto err_out;
+ }
+
+ buf_pos += hifn_setup_base_command(dev, buf_pos, nbytes,
+ nbytes, mask, dev->snum);
+
+ if (ctx->op == ACRYPTO_OP_ENCRYPT || ctx->op == ACRYPTO_OP_DECRYPT) {
+ u16 md = 0;
+
+ if (ctx->keysize)
+ md |= HIFN_CRYPT_CMD_NEW_KEY;
+ if (ctx->iv && ctx->mode != ACRYPTO_MODE_ECB)
+ md |= HIFN_CRYPT_CMD_NEW_IV;
+
+ switch (ctx->mode) {
+ case ACRYPTO_MODE_ECB:
+ md |= HIFN_CRYPT_CMD_MODE_ECB;
+ break;
+ case ACRYPTO_MODE_CBC:
+ md |= HIFN_CRYPT_CMD_MODE_CBC;
+ break;
+ case ACRYPTO_MODE_CFB:
+ md |= HIFN_CRYPT_CMD_MODE_CFB;
+ break;
+ case ACRYPTO_MODE_OFB:
+ md |= HIFN_CRYPT_CMD_MODE_OFB;
+ break;
+ default:
+ goto err_out;
+ }
+
+ switch (ctx->type) {
+ case ACRYPTO_TYPE_AES_128:
+ if (ctx->keysize != 16)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_KSZ_128 |
+ HIFN_CRYPT_CMD_ALG_AES;
+ break;
+ case ACRYPTO_TYPE_AES_192:
+ if (ctx->keysize != 24)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_KSZ_192 |
+ HIFN_CRYPT_CMD_ALG_AES;
+ break;
+ case ACRYPTO_TYPE_AES_256:
+ if (ctx->keysize != 32)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_KSZ_256 |
+ HIFN_CRYPT_CMD_ALG_AES;
+ break;
+ case ACRYPTO_TYPE_3DES:
+ if (ctx->keysize != 24)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_ALG_3DES;
+ break;
+ case ACRYPTO_TYPE_DES:
+ if (ctx->keysize != 8)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_ALG_DES;
+ break;
+ default:
+ goto err_out;
+ }
+
+ buf_pos += hifn_setup_crypto_command(dev, buf_pos,
+ nbytes, nbytes, ctx->key, ctx->keysize,
+ ctx->iv, ctx->ivsize, md);
+ }
+
+ dev->sa[sa_idx] = priv;
+
+ cmd_len = buf_pos - buf;
+ dma->cmdr[dma->cmdi].l = __cpu_to_le32(cmd_len | HIFN_D_VALID |
+ HIFN_D_LAST | HIFN_D_MASKDONEIRQ);
+
+ if (++dma->cmdi == HIFN_D_CMD_RSIZE) {
+ dma->cmdr[dma->cmdi].l = __cpu_to_le32(HIFN_MAX_COMMAND |
+ HIFN_D_VALID | HIFN_D_LAST |
+ HIFN_D_MASKDONEIRQ | HIFN_D_JUMP);
+ dma->cmdi = 0;
+ } else
+ dma->cmdr[dma->cmdi-1].l |= __cpu_to_le32(HIFN_D_VALID);
+
+ if (!(dev->flags & HIFN_FLAG_CMD_BUSY)) {
+ hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_C_CTRL_ENA);
+ dev->flags |= HIFN_FLAG_CMD_BUSY;
+ }
+
+ hifn_setup_dst_desc(dev, dpage, doff, nbytes);
+ hifn_setup_res_desc(dev);
+
+ return 0;
+
+err_out:
+ return -EINVAL;
+}
+
+static int ablkcipher_walk_init(struct ablkcipher_walk *w,
+ int num, gfp_t gfp_flags)
+{
+ int i;
+
+ num = min(ASYNC_SCATTERLIST_CACHE, num);
+ sg_init_table(w->cache, num);
+
+ w->num = 0;
+ for (i=0; i<num; ++i) {
+ struct page *page = alloc_page(gfp_flags);
+ struct scatterlist *s;
+
+ if (!page)
+ break;
+
+ s = &w->cache[i];
+
+ sg_set_page(s, page, PAGE_SIZE, 0);
+ w->num++;
+ }
+
+ return i;
+}
+
+static void ablkcipher_walk_exit(struct ablkcipher_walk *w)
+{
+ int i;
+
+ for (i=0; i<w->num; ++i) {
+ struct scatterlist *s = &w->cache[i];
+
+ __free_page(sg_page(s));
+
+ s->length = 0;
+ }
+
+ w->num = 0;
+}
+
+static int ablkcipher_add(void *daddr, unsigned int *drestp, struct scatterlist *src,
+ unsigned int size, unsigned int *nbytesp)
+{
+ unsigned int copy, drest = *drestp, nbytes = *nbytesp;
+ int idx = 0;
+ void *saddr;
+
+ if (drest < size || size > nbytes)
+ return -EINVAL;
+
+ while (size) {
+ copy = min(drest, src->length);
+
+ saddr = kmap_atomic(sg_page(src), KM_SOFTIRQ1);
+ memcpy(daddr, saddr + src->offset, copy);
+ kunmap_atomic(saddr, KM_SOFTIRQ1);
+
+ size -= copy;
+ drest -= copy;
+ nbytes -= copy;
+ daddr += copy;
+
+ dprintk("%s: copy: %u, size: %u, drest: %u, nbytes: %u.\n",
+ __func__, copy, size, drest, nbytes);
+
+ src++;
+ idx++;
+ }
+
+ *nbytesp = nbytes;
+ *drestp = drest;
+
+ return idx;
+}
+
+static int ablkcipher_walk(struct ablkcipher_request *req,
+ struct ablkcipher_walk *w)
+{
+ unsigned blocksize =
+ crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(req));
+ unsigned alignmask =
+ crypto_ablkcipher_alignmask(crypto_ablkcipher_reqtfm(req));
+ struct scatterlist *src, *dst, *t;
+ void *daddr;
+ unsigned int nbytes = req->nbytes, offset, copy, diff;
+ int idx, tidx, err;
+
+ tidx = idx = 0;
+ offset = 0;
+ while (nbytes) {
+ if (idx >= w->num && (w->flags & ASYNC_FLAGS_MISALIGNED))
+ return -EINVAL;
+
+ src = &req->src[idx];
+ dst = &req->dst[idx];
+
+ dprintk("\n%s: slen: %u, dlen: %u, soff: %u, doff: %u, offset: %u, "
+ "blocksize: %u, nbytes: %u.\n",
+ __func__, src->length, dst->length, src->offset,
+ dst->offset, offset, blocksize, nbytes);
+
+ if (src->length & (blocksize - 1) ||
+ src->offset & (alignmask - 1) ||
+ dst->length & (blocksize - 1) ||
+ dst->offset & (alignmask - 1) ||
+ offset) {
+ unsigned slen = src->length - offset;
+ unsigned dlen = PAGE_SIZE;
+
+ t = &w->cache[idx];
+
+ daddr = kmap_atomic(sg_page(t), KM_SOFTIRQ0);
+ err = ablkcipher_add(daddr, &dlen, src, slen, &nbytes);
+ if (err < 0)
+ goto err_out_unmap;
+
+ idx += err;
+
+ copy = slen & ~(blocksize - 1);
+ diff = slen & (blocksize - 1);
+
+ if (dlen < nbytes) {
+ /*
+ * Destination page does not have enough space
+ * to put there additional blocksized chunk,
+ * so we mark that page as containing only
+ * blocksize aligned chunks:
+ * t->length = (slen & ~(blocksize - 1));
+ * and increase number of bytes to be processed
+ * in next chunk:
+ * nbytes += diff;
+ */
+ nbytes += diff;
+
+ /*
+ * Temporary of course...
+ * Kick author if you will catch this one.
+ */
+ printk(KERN_ERR "%s: dlen: %u, nbytes: %u,"
+ "slen: %u, offset: %u.\n",
+ __func__, dlen, nbytes, slen, offset);
+ printk(KERN_ERR "%s: please contact author to fix this "
+ "issue, generally you should not catch "
+ "this path under any condition but who "
+ "knows how did you use crypto code.\n"
+ "Thank you.\n", __func__);
+ BUG();
+ } else {
+ copy += diff + nbytes;
+
+ src = &req->src[idx];
+
+ err = ablkcipher_add(daddr + slen, &dlen, src, nbytes, &nbytes);
+ if (err < 0)
+ goto err_out_unmap;
+
+ idx += err;
+ }
+
+ t->length = copy;
+ t->offset = offset;
+
+ kunmap_atomic(daddr, KM_SOFTIRQ0);
+ } else {
+ nbytes -= src->length;
+ idx++;
+ }
+
+ tidx++;
+ }
+
+ return tidx;
+
+err_out_unmap:
+ kunmap_atomic(daddr, KM_SOFTIRQ0);
+ return err;
+}
+
+static int hifn_setup_session(struct ablkcipher_request *req)
+{
+ struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct hifn_device *dev = ctx->dev;
+ struct page *spage, *dpage;
+ unsigned long soff, doff, flags;
+ unsigned int nbytes = req->nbytes, idx = 0, len;
+ int err = -EINVAL, sg_num;
+ struct scatterlist *src, *dst, *t;
+ unsigned blocksize =
+ crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(req));
+ unsigned alignmask =
+ crypto_ablkcipher_alignmask(crypto_ablkcipher_reqtfm(req));
+
+ if (ctx->iv && !ctx->ivsize && ctx->mode != ACRYPTO_MODE_ECB)
+ goto err_out_exit;
+
+ ctx->walk.flags = 0;
+
+ while (nbytes) {
+ src = &req->src[idx];
+ dst = &req->dst[idx];
+
+ if (src->length & (blocksize - 1) ||
+ src->offset & (alignmask - 1) ||
+ dst->length & (blocksize - 1) ||
+ dst->offset & (alignmask - 1)) {
+ ctx->walk.flags |= ASYNC_FLAGS_MISALIGNED;
+ }
+
+ nbytes -= src->length;
+ idx++;
+ }
+
+ if (ctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
+ err = ablkcipher_walk_init(&ctx->walk, idx, GFP_ATOMIC);
+ if (err < 0)
+ return err;
+ }
+
+ nbytes = req->nbytes;
+ idx = 0;
+
+ sg_num = ablkcipher_walk(req, &ctx->walk);
+
+ atomic_set(&ctx->sg_num, sg_num);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->started + sg_num > HIFN_QUEUE_LENGTH) {
+ err = -EAGAIN;
+ goto err_out;
+ }
+
+ dev->snum++;
+ dev->started += sg_num;
+
+ while (nbytes) {
+ src = &req->src[idx];
+ dst = &req->dst[idx];
+ t = &ctx->walk.cache[idx];
+
+ if (t->length) {
+ spage = dpage = sg_page(t);
+ soff = doff = 0;
+ len = t->length;
+ } else {
+ spage = sg_page(src);
+ soff = src->offset;
+
+ dpage = sg_page(dst);
+ doff = dst->offset;
+
+ len = dst->length;
+ }
+
+ idx++;
+
+ err = hifn_setup_dma(dev, spage, soff, dpage, doff, nbytes,
+ req, ctx);
+ if (err)
+ goto err_out;
+
+ nbytes -= len;
+ }
+
+ dev->active = HIFN_DEFAULT_ACTIVE_NUM;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+
+err_out:
+ spin_unlock_irqrestore(&dev->lock, flags);
+err_out_exit:
+ if (err && printk_ratelimit())
+ dprintk("%s: iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
+ "type: %u, err: %d.\n",
+ dev->name, ctx->iv, ctx->ivsize,
+ ctx->key, ctx->keysize,
+ ctx->mode, ctx->op, ctx->type, err);
+
+ return err;
+}
+
+static int hifn_test(struct hifn_device *dev, int encdec, u8 snum)
+{
+ int n, err;
+ u8 src[16];
+ struct hifn_context ctx;
+ u8 fips_aes_ecb_from_zero[16] = {
+ 0x66, 0xE9, 0x4B, 0xD4,
+ 0xEF, 0x8A, 0x2C, 0x3B,
+ 0x88, 0x4C, 0xFA, 0x59,
+ 0xCA, 0x34, 0x2B, 0x2E};
+
+ memset(src, 0, sizeof(src));
+ memset(ctx.key, 0, sizeof(ctx.key));
+
+ ctx.dev = dev;
+ ctx.keysize = 16;
+ ctx.ivsize = 0;
+ ctx.iv = NULL;
+ ctx.op = (encdec)?ACRYPTO_OP_ENCRYPT:ACRYPTO_OP_DECRYPT;
+ ctx.mode = ACRYPTO_MODE_ECB;
+ ctx.type = ACRYPTO_TYPE_AES_128;
+ atomic_set(&ctx.sg_num, 1);
+
+ err = hifn_setup_dma(dev,
+ virt_to_page(src), offset_in_page(src),
+ virt_to_page(src), offset_in_page(src),
+ sizeof(src), NULL, &ctx);
+ if (err)
+ goto err_out;
+
+ msleep(200);
+
+ dprintk("%s: decoded: ", dev->name);
+ for (n=0; n<sizeof(src); ++n)
+ dprintk("%02x ", src[n]);
+ dprintk("\n");
+ dprintk("%s: FIPS : ", dev->name);
+ for (n=0; n<sizeof(fips_aes_ecb_from_zero); ++n)
+ dprintk("%02x ", fips_aes_ecb_from_zero[n]);
+ dprintk("\n");
+
+ if (!memcmp(src, fips_aes_ecb_from_zero, sizeof(fips_aes_ecb_from_zero))) {
+ printk(KERN_INFO "%s: AES 128 ECB test has been successfully "
+ "passed.\n", dev->name);
+ return 0;
+ }
+
+err_out:
+ printk(KERN_INFO "%s: AES 128 ECB test has been failed.\n", dev->name);
+ return -1;
+}
+
+static int hifn_start_device(struct hifn_device *dev)
+{
+ int err;
+
+ hifn_reset_dma(dev, 1);
+
+ err = hifn_enable_crypto(dev);
+ if (err)
+ return err;
+
+ hifn_reset_puc(dev);
+
+ hifn_init_dma(dev);
+
+ hifn_init_registers(dev);
+
+ hifn_init_pubrng(dev);
+
+ return 0;
+}
+
+static int ablkcipher_get(void *saddr, unsigned int *srestp, unsigned int offset,
+ struct scatterlist *dst, unsigned int size, unsigned int *nbytesp)
+{
+ unsigned int srest = *srestp, nbytes = *nbytesp, copy;
+ void *daddr;
+ int idx = 0;
+
+ if (srest < size || size > nbytes)
+ return -EINVAL;
+
+ while (size) {
+
+ copy = min(dst->length, srest);
+
+ daddr = kmap_atomic(sg_page(dst), KM_IRQ0);
+ memcpy(daddr + dst->offset + offset, saddr, copy);
+ kunmap_atomic(daddr, KM_IRQ0);
+
+ nbytes -= copy;
+ size -= copy;
+ srest -= copy;
+ saddr += copy;
+ offset = 0;
+
+ dprintk("%s: copy: %u, size: %u, srest: %u, nbytes: %u.\n",
+ __func__, copy, size, srest, nbytes);
+
+ dst++;
+ idx++;
+ }
+
+ *nbytesp = nbytes;
+ *srestp = srest;
+
+ return idx;
+}
+
+static void hifn_process_ready(struct ablkcipher_request *req, int error)
+{
+ struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct hifn_device *dev;
+
+ dprintk("%s: req: %p, ctx: %p.\n", __func__, req, ctx);
+
+ dev = ctx->dev;
+ dprintk("%s: req: %p, started: %d, sg_num: %d.\n",
+ __func__, req, dev->started, atomic_read(&ctx->sg_num));
+
+ if (--dev->started < 0)
+ BUG();
+
+ if (atomic_dec_and_test(&ctx->sg_num)) {
+ unsigned int nbytes = req->nbytes;
+ int idx = 0, err;
+ struct scatterlist *dst, *t;
+ void *saddr;
+
+ if (ctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
+ while (nbytes) {
+ t = &ctx->walk.cache[idx];
+ dst = &req->dst[idx];
+
+ dprintk("\n%s: sg_page(t): %p, t->length: %u, "
+ "sg_page(dst): %p, dst->length: %u, "
+ "nbytes: %u.\n",
+ __func__, sg_page(t), t->length,
+ sg_page(dst), dst->length, nbytes);
+
+ if (!t->length) {
+ nbytes -= dst->length;
+ idx++;
+ continue;
+ }
+
+ saddr = kmap_atomic(sg_page(t), KM_IRQ1);
+
+ err = ablkcipher_get(saddr, &t->length, t->offset,
+ dst, nbytes, &nbytes);
+ if (err < 0) {
+ kunmap_atomic(saddr, KM_IRQ1);
+ break;
+ }
+
+ idx += err;
+ kunmap_atomic(saddr, KM_IRQ1);
+ }
+
+ ablkcipher_walk_exit(&ctx->walk);
+ }
+
+ req->base.complete(&req->base, error);
+ }
+}
+
+static void hifn_check_for_completion(struct hifn_device *dev, int error)
+{
+ int i;
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+
+ for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
+ struct hifn_desc *d = &dma->resr[i];
+
+ if (!(d->l & __cpu_to_le32(HIFN_D_VALID)) && dev->sa[i]) {
+ dev->success++;
+ dev->reset = 0;
+ hifn_process_ready(dev->sa[i], error);
+ dev->sa[i] = NULL;
+ }
+
+ if (d->l & __cpu_to_le32(HIFN_D_DESTOVER | HIFN_D_OVER))
+ if (printk_ratelimit())
+ printk("%s: overflow detected [d: %u, o: %u] "
+ "at %d resr: l: %08x, p: %08x.\n",
+ dev->name,
+ !!(d->l & __cpu_to_le32(HIFN_D_DESTOVER)),
+ !!(d->l & __cpu_to_le32(HIFN_D_OVER)),
+ i, d->l, d->p);
+ }
+}
+
+static void hifn_clear_rings(struct hifn_device *dev)
+{
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ int i, u;
+
+ dprintk("%s: ring cleanup 1: i: %d.%d.%d.%d, u: %d.%d.%d.%d, "
+ "k: %d.%d.%d.%d.\n",
+ dev->name,
+ dma->cmdi, dma->srci, dma->dsti, dma->resi,
+ dma->cmdu, dma->srcu, dma->dstu, dma->resu,
+ dma->cmdk, dma->srck, dma->dstk, dma->resk);
+
+ i = dma->resk; u = dma->resu;
+ while (u != 0) {
+ if (dma->resr[i].l & __cpu_to_le32(HIFN_D_VALID))
+ break;
+
+ if (i != HIFN_D_RES_RSIZE)
+ u--;
+
+ if (++i == (HIFN_D_RES_RSIZE + 1))
+ i = 0;
+ }
+ dma->resk = i; dma->resu = u;
+
+ i = dma->srck; u = dma->srcu;
+ while (u != 0) {
+ if (i == HIFN_D_SRC_RSIZE)
+ i = 0;
+ if (dma->srcr[i].l & __cpu_to_le32(HIFN_D_VALID))
+ break;
+ i++, u--;
+ }
+ dma->srck = i; dma->srcu = u;
+
+ i = dma->cmdk; u = dma->cmdu;
+ while (u != 0) {
+ if (dma->cmdr[i].l & __cpu_to_le32(HIFN_D_VALID))
+ break;
+ if (i != HIFN_D_CMD_RSIZE)
+ u--;
+ if (++i == (HIFN_D_CMD_RSIZE + 1))
+ i = 0;
+ }
+ dma->cmdk = i; dma->cmdu = u;
+
+ i = dma->dstk; u = dma->dstu;
+ while (u != 0) {
+ if (i == HIFN_D_DST_RSIZE)
+ i = 0;
+ if (dma->dstr[i].l & __cpu_to_le32(HIFN_D_VALID))
+ break;
+ i++, u--;
+ }
+ dma->dstk = i; dma->dstu = u;
+
+ dprintk("%s: ring cleanup 2: i: %d.%d.%d.%d, u: %d.%d.%d.%d, "
+ "k: %d.%d.%d.%d.\n",
+ dev->name,
+ dma->cmdi, dma->srci, dma->dsti, dma->resi,
+ dma->cmdu, dma->srcu, dma->dstu, dma->resu,
+ dma->cmdk, dma->srck, dma->dstk, dma->resk);
+}
+
+static void hifn_work(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct hifn_device *dev = container_of(dw, struct hifn_device, work);
+ unsigned long flags;
+ int reset = 0;
+ u32 r = 0;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->active == 0) {
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+
+ if (dma->cmdu == 0 && (dev->flags & HIFN_FLAG_CMD_BUSY)) {
+ dev->flags &= ~HIFN_FLAG_CMD_BUSY;
+ r |= HIFN_DMACSR_C_CTRL_DIS;
+ }
+ if (dma->srcu == 0 && (dev->flags & HIFN_FLAG_SRC_BUSY)) {
+ dev->flags &= ~HIFN_FLAG_SRC_BUSY;
+ r |= HIFN_DMACSR_S_CTRL_DIS;
+ }
+ if (dma->dstu == 0 && (dev->flags & HIFN_FLAG_DST_BUSY)) {
+ dev->flags &= ~HIFN_FLAG_DST_BUSY;
+ r |= HIFN_DMACSR_D_CTRL_DIS;
+ }
+ if (dma->resu == 0 && (dev->flags & HIFN_FLAG_RES_BUSY)) {
+ dev->flags &= ~HIFN_FLAG_RES_BUSY;
+ r |= HIFN_DMACSR_R_CTRL_DIS;
+ }
+ if (r)
+ hifn_write_1(dev, HIFN_1_DMA_CSR, r);
+ } else
+ dev->active--;
+
+ if (dev->prev_success == dev->success && dev->started)
+ reset = 1;
+ dev->prev_success = dev->success;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (reset) {
+ dprintk("%s: r: %08x, active: %d, started: %d, "
+ "success: %lu: reset: %d.\n",
+ dev->name, r, dev->active, dev->started,
+ dev->success, reset);
+
+ if (++dev->reset >= 5) {
+ dprintk("%s: really hard reset.\n", dev->name);
+ hifn_reset_dma(dev, 1);
+ hifn_stop_device(dev);
+ hifn_start_device(dev);
+ dev->reset = 0;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ hifn_check_for_completion(dev, -EBUSY);
+ hifn_clear_rings(dev);
+ dev->started = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+
+ schedule_delayed_work(&dev->work, HZ);
+}
+
+static irqreturn_t hifn_interrupt(int irq, void *data)
+{
+ struct hifn_device *dev = (struct hifn_device *)data;
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ u32 dmacsr, restart;
+
+ dmacsr = hifn_read_1(dev, HIFN_1_DMA_CSR);
+
+ dprintk("%s: 1 dmacsr: %08x, dmareg: %08x, res: %08x [%d], "
+ "i: %d.%d.%d.%d, u: %d.%d.%d.%d.\n",
+ dev->name, dmacsr, dev->dmareg, dmacsr & dev->dmareg, dma->cmdi,
+ dma->cmdu, dma->srcu, dma->dstu, dma->resu,
+ dma->cmdi, dma->srci, dma->dsti, dma->resi);
+
+ if ((dmacsr & dev->dmareg) == 0)
+ return IRQ_NONE;
+
+ hifn_write_1(dev, HIFN_1_DMA_CSR, dmacsr & dev->dmareg);
+
+ if (dmacsr & HIFN_DMACSR_ENGINE)
+ hifn_write_0(dev, HIFN_0_PUISR, hifn_read_0(dev, HIFN_0_PUISR));
+ if (dmacsr & HIFN_DMACSR_PUBDONE)
+ hifn_write_1(dev, HIFN_1_PUB_STATUS,
+ hifn_read_1(dev, HIFN_1_PUB_STATUS) | HIFN_PUBSTS_DONE);
+
+ restart = dmacsr & (HIFN_DMACSR_R_OVER | HIFN_DMACSR_D_OVER);
+ if (restart) {
+ u32 puisr = hifn_read_0(dev, HIFN_0_PUISR);
+
+ if (printk_ratelimit())
+ printk("%s: overflow: r: %d, d: %d, puisr: %08x, d: %u.\n",
+ dev->name, !!(dmacsr & HIFN_DMACSR_R_OVER),
+ !!(dmacsr & HIFN_DMACSR_D_OVER),
+ puisr, !!(puisr & HIFN_PUISR_DSTOVER));
+ if (!!(puisr & HIFN_PUISR_DSTOVER))
+ hifn_write_0(dev, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
+ hifn_write_1(dev, HIFN_1_DMA_CSR, dmacsr & (HIFN_DMACSR_R_OVER |
+ HIFN_DMACSR_D_OVER));
+ }
+
+ restart = dmacsr & (HIFN_DMACSR_C_ABORT | HIFN_DMACSR_S_ABORT |
+ HIFN_DMACSR_D_ABORT | HIFN_DMACSR_R_ABORT);
+ if (restart) {
+ if (printk_ratelimit())
+ printk("%s: abort: c: %d, s: %d, d: %d, r: %d.\n",
+ dev->name, !!(dmacsr & HIFN_DMACSR_C_ABORT),
+ !!(dmacsr & HIFN_DMACSR_S_ABORT),
+ !!(dmacsr & HIFN_DMACSR_D_ABORT),
+ !!(dmacsr & HIFN_DMACSR_R_ABORT));
+ hifn_reset_dma(dev, 1);
+ hifn_init_dma(dev);
+ hifn_init_registers(dev);
+ }
+
+ if ((dmacsr & HIFN_DMACSR_C_WAIT) && (dma->cmdu == 0)) {
+ dprintk("%s: wait on command.\n", dev->name);
+ dev->dmareg &= ~(HIFN_DMAIER_C_WAIT);
+ hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
+ }
+
+ tasklet_schedule(&dev->tasklet);
+ hifn_clear_rings(dev);
+
+ return IRQ_HANDLED;
+}
+
+static void hifn_flush(struct hifn_device *dev)
+{
+ unsigned long flags;
+ struct crypto_async_request *async_req;
+ struct hifn_context *ctx;
+ struct ablkcipher_request *req;
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
+ struct hifn_desc *d = &dma->resr[i];
+
+ if (dev->sa[i]) {
+ hifn_process_ready(dev->sa[i],
+ (d->l & __cpu_to_le32(HIFN_D_VALID))?-ENODEV:0);
+ }
+ }
+
+ while ((async_req = crypto_dequeue_request(&dev->queue))) {
+ ctx = crypto_tfm_ctx(async_req->tfm);
+ req = container_of(async_req, struct ablkcipher_request, base);
+
+ hifn_process_ready(req, -ENODEV);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int hifn_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int len)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct hifn_context *ctx = crypto_tfm_ctx(tfm);
+ struct hifn_device *dev = ctx->dev;
+
+ if (len > HIFN_MAX_CRYPT_KEY_LENGTH) {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -1;
+ }
+
+ if (len == HIFN_DES_KEY_LENGTH) {
+ u32 tmp[DES_EXPKEY_WORDS];
+ int ret = des_ekey(tmp, key);
+
+ if (unlikely(ret == 0) && (tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ return -EINVAL;
+ }
+ }
+
+ dev->flags &= ~HIFN_FLAG_OLD_KEY;
+
+ memcpy(ctx->key, key, len);
+ ctx->keysize = len;
+
+ return 0;
+}
+
+static int hifn_handle_req(struct ablkcipher_request *req)
+{
+ struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct hifn_device *dev = ctx->dev;
+ int err = -EAGAIN;
+
+ if (dev->started + DIV_ROUND_UP(req->nbytes, PAGE_SIZE) <= HIFN_QUEUE_LENGTH)
+ err = hifn_setup_session(req);
+
+ if (err == -EAGAIN) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ err = ablkcipher_enqueue_request(&dev->queue, req);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+
+ return err;
+}
+
+static int hifn_setup_crypto_req(struct ablkcipher_request *req, u8 op,
+ u8 type, u8 mode)
+{
+ struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+ unsigned ivsize;
+
+ ivsize = crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(req));
+
+ if (req->info && mode != ACRYPTO_MODE_ECB) {
+ if (type == ACRYPTO_TYPE_AES_128)
+ ivsize = HIFN_AES_IV_LENGTH;
+ else if (type == ACRYPTO_TYPE_DES)
+ ivsize = HIFN_DES_KEY_LENGTH;
+ else if (type == ACRYPTO_TYPE_3DES)
+ ivsize = HIFN_3DES_KEY_LENGTH;
+ }
+
+ if (ctx->keysize != 16 && type == ACRYPTO_TYPE_AES_128) {
+ if (ctx->keysize == 24)
+ type = ACRYPTO_TYPE_AES_192;
+ else if (ctx->keysize == 32)
+ type = ACRYPTO_TYPE_AES_256;
+ }
+
+ ctx->op = op;
+ ctx->mode = mode;
+ ctx->type = type;
+ ctx->iv = req->info;
+ ctx->ivsize = ivsize;
+
+ /*
+ * HEAVY TODO: needs to kick Herbert XU to write documentation.
+ * HEAVY TODO: needs to kick Herbert XU to write documentation.
+ * HEAVY TODO: needs to kick Herbert XU to write documentation.
+ */
+
+ return hifn_handle_req(req);
+}
+
+static int hifn_process_queue(struct hifn_device *dev)
+{
+ struct crypto_async_request *async_req;
+ struct hifn_context *ctx;
+ struct ablkcipher_request *req;
+ unsigned long flags;
+ int err = 0;
+
+ while (dev->started < HIFN_QUEUE_LENGTH) {
+ spin_lock_irqsave(&dev->lock, flags);
+ async_req = crypto_dequeue_request(&dev->queue);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (!async_req)
+ break;
+
+ ctx = crypto_tfm_ctx(async_req->tfm);
+ req = container_of(async_req, struct ablkcipher_request, base);
+
+ err = hifn_handle_req(req);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int hifn_setup_crypto(struct ablkcipher_request *req, u8 op,
+ u8 type, u8 mode)
+{
+ int err;
+ struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct hifn_device *dev = ctx->dev;
+
+ err = hifn_setup_crypto_req(req, op, type, mode);
+ if (err)
+ return err;
+
+ if (dev->started < HIFN_QUEUE_LENGTH && dev->queue.qlen)
+ err = hifn_process_queue(dev);
+
+ return err;
+}
+
+/*
+ * AES ecryption functions.
+ */
+static inline int hifn_encrypt_aes_ecb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_ECB);
+}
+static inline int hifn_encrypt_aes_cbc(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_CBC);
+}
+static inline int hifn_encrypt_aes_cfb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_CFB);
+}
+static inline int hifn_encrypt_aes_ofb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_OFB);
+}
+
+/*
+ * AES decryption functions.
+ */
+static inline int hifn_decrypt_aes_ecb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_ECB);
+}
+static inline int hifn_decrypt_aes_cbc(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_CBC);
+}
+static inline int hifn_decrypt_aes_cfb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_CFB);
+}
+static inline int hifn_decrypt_aes_ofb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_AES_128, ACRYPTO_MODE_OFB);
+}
+
+/*
+ * DES ecryption functions.
+ */
+static inline int hifn_encrypt_des_ecb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_ECB);
+}
+static inline int hifn_encrypt_des_cbc(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_CBC);
+}
+static inline int hifn_encrypt_des_cfb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_CFB);
+}
+static inline int hifn_encrypt_des_ofb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_OFB);
+}
+
+/*
+ * DES decryption functions.
+ */
+static inline int hifn_decrypt_des_ecb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_ECB);
+}
+static inline int hifn_decrypt_des_cbc(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_CBC);
+}
+static inline int hifn_decrypt_des_cfb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_CFB);
+}
+static inline int hifn_decrypt_des_ofb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_DES, ACRYPTO_MODE_OFB);
+}
+
+/*
+ * 3DES ecryption functions.
+ */
+static inline int hifn_encrypt_3des_ecb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_ECB);
+}
+static inline int hifn_encrypt_3des_cbc(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_CBC);
+}
+static inline int hifn_encrypt_3des_cfb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_CFB);
+}
+static inline int hifn_encrypt_3des_ofb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_ENCRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_OFB);
+}
+
+/*
+ * 3DES decryption functions.
+ */
+static inline int hifn_decrypt_3des_ecb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_ECB);
+}
+static inline int hifn_decrypt_3des_cbc(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_CBC);
+}
+static inline int hifn_decrypt_3des_cfb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_CFB);
+}
+static inline int hifn_decrypt_3des_ofb(struct ablkcipher_request *req)
+{
+ return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
+ ACRYPTO_TYPE_3DES, ACRYPTO_MODE_OFB);
+}
+
+struct hifn_alg_template
+{
+ char name[CRYPTO_MAX_ALG_NAME];
+ char drv_name[CRYPTO_MAX_ALG_NAME];
+ unsigned int bsize;
+ struct ablkcipher_alg ablkcipher;
+};
+
+static struct hifn_alg_template hifn_alg_templates[] = {
+ /*
+ * 3DES ECB, CBC, CFB and OFB modes.
+ */
+ {
+ .name = "cfb(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_3DES_KEY_LENGTH,
+ .max_keysize = HIFN_3DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_3des_cfb,
+ .decrypt = hifn_decrypt_3des_cfb,
+ },
+ },
+ {
+ .name = "ofb(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_3DES_KEY_LENGTH,
+ .max_keysize = HIFN_3DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_3des_ofb,
+ .decrypt = hifn_decrypt_3des_ofb,
+ },
+ },
+ {
+ .name = "cbc(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_3DES_KEY_LENGTH,
+ .max_keysize = HIFN_3DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_3des_cbc,
+ .decrypt = hifn_decrypt_3des_cbc,
+ },
+ },
+ {
+ .name = "ecb(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_3DES_KEY_LENGTH,
+ .max_keysize = HIFN_3DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_3des_ecb,
+ .decrypt = hifn_decrypt_3des_ecb,
+ },
+ },
+
+ /*
+ * DES ECB, CBC, CFB and OFB modes.
+ */
+ {
+ .name = "cfb(des)", .drv_name = "hifn-des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_DES_KEY_LENGTH,
+ .max_keysize = HIFN_DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_des_cfb,
+ .decrypt = hifn_decrypt_des_cfb,
+ },
+ },
+ {
+ .name = "ofb(des)", .drv_name = "hifn-des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_DES_KEY_LENGTH,
+ .max_keysize = HIFN_DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_des_ofb,
+ .decrypt = hifn_decrypt_des_ofb,
+ },
+ },
+ {
+ .name = "cbc(des)", .drv_name = "hifn-des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_DES_KEY_LENGTH,
+ .max_keysize = HIFN_DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_des_cbc,
+ .decrypt = hifn_decrypt_des_cbc,
+ },
+ },
+ {
+ .name = "ecb(des)", .drv_name = "hifn-des", .bsize = 8,
+ .ablkcipher = {
+ .min_keysize = HIFN_DES_KEY_LENGTH,
+ .max_keysize = HIFN_DES_KEY_LENGTH,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_des_ecb,
+ .decrypt = hifn_decrypt_des_ecb,
+ },
+ },
+
+ /*
+ * AES ECB, CBC, CFB and OFB modes.
+ */
+ {
+ .name = "ecb(aes)", .drv_name = "hifn-aes", .bsize = 16,
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_aes_ecb,
+ .decrypt = hifn_decrypt_aes_ecb,
+ },
+ },
+ {
+ .name = "cbc(aes)", .drv_name = "hifn-aes", .bsize = 16,
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_aes_cbc,
+ .decrypt = hifn_decrypt_aes_cbc,
+ },
+ },
+ {
+ .name = "cfb(aes)", .drv_name = "hifn-aes", .bsize = 16,
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_aes_cfb,
+ .decrypt = hifn_decrypt_aes_cfb,
+ },
+ },
+ {
+ .name = "ofb(aes)", .drv_name = "hifn-aes", .bsize = 16,
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = hifn_setkey,
+ .encrypt = hifn_encrypt_aes_ofb,
+ .decrypt = hifn_decrypt_aes_ofb,
+ },
+ },
+};
+
+static int hifn_cra_init(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct hifn_crypto_alg *ha = crypto_alg_to_hifn(alg);
+ struct hifn_context *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->dev = ha->dev;
+
+ return 0;
+}
+
+static int hifn_alg_alloc(struct hifn_device *dev, struct hifn_alg_template *t)
+{
+ struct hifn_crypto_alg *alg;
+ int err;
+
+ alg = kzalloc(sizeof(struct hifn_crypto_alg), GFP_KERNEL);
+ if (!alg)
+ return -ENOMEM;
+
+ snprintf(alg->alg.cra_name, CRYPTO_MAX_ALG_NAME, "%s", t->name);
+ snprintf(alg->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", t->drv_name);
+
+ alg->alg.cra_priority = 300;
+ alg->alg.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC;
+ alg->alg.cra_blocksize = t->bsize;
+ alg->alg.cra_ctxsize = sizeof(struct hifn_context);
+ alg->alg.cra_alignmask = 15;
+ if (t->bsize == 8)
+ alg->alg.cra_alignmask = 3;
+ alg->alg.cra_type = &crypto_ablkcipher_type;
+ alg->alg.cra_module = THIS_MODULE;
+ alg->alg.cra_u.ablkcipher = t->ablkcipher;
+ alg->alg.cra_init = hifn_cra_init;
+
+ alg->dev = dev;
+
+ list_add_tail(&alg->entry, &dev->alg_list);
+
+ err = crypto_register_alg(&alg->alg);
+ if (err) {
+ list_del(&alg->entry);
+ kfree(alg);
+ }
+
+ return err;
+}
+
+static void hifn_unregister_alg(struct hifn_device *dev)
+{
+ struct hifn_crypto_alg *a, *n;
+
+ list_for_each_entry_safe(a, n, &dev->alg_list, entry) {
+ list_del(&a->entry);
+ crypto_unregister_alg(&a->alg);
+ kfree(a);
+ }
+}
+
+static int hifn_register_alg(struct hifn_device *dev)
+{
+ int i, err;
+
+ for (i=0; i<ARRAY_SIZE(hifn_alg_templates); ++i) {
+ err = hifn_alg_alloc(dev, &hifn_alg_templates[i]);
+ if (err)
+ goto err_out_exit;
+ }
+
+ return 0;
+
+err_out_exit:
+ hifn_unregister_alg(dev);
+ return err;
+}
+
+static void hifn_tasklet_callback(unsigned long data)
+{
+ struct hifn_device *dev = (struct hifn_device *)data;
+
+ /*
+ * This is ok to call this without lock being held,
+ * althogh it modifies some parameters used in parallel,
+ * (like dev->success), but they are used in process
+ * context or update is atomic (like setting dev->sa[i] to NULL).
+ */
+ hifn_check_for_completion(dev, 0);
+}
+
+static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int err, i;
+ struct hifn_device *dev;
+ char name[8];
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+ pci_set_master(pdev);
+
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err)
+ goto err_out_disable_pci_device;
+
+ snprintf(name, sizeof(name), "hifn%d",
+ atomic_inc_return(&hifn_dev_number)-1);
+
+ err = pci_request_regions(pdev, name);
+ if (err)
+ goto err_out_disable_pci_device;
+
+ if (pci_resource_len(pdev, 0) < HIFN_BAR0_SIZE ||
+ pci_resource_len(pdev, 1) < HIFN_BAR1_SIZE ||
+ pci_resource_len(pdev, 2) < HIFN_BAR2_SIZE) {
+ dprintk("%s: Broken hardware - I/O regions are too small.\n",
+ pci_name(pdev));
+ err = -ENODEV;
+ goto err_out_free_regions;
+ }
+
+ dev = kzalloc(sizeof(struct hifn_device) + sizeof(struct crypto_alg),
+ GFP_KERNEL);
+ if (!dev) {
+ err = -ENOMEM;
+ goto err_out_free_regions;
+ }
+
+ INIT_LIST_HEAD(&dev->alg_list);
+
+ snprintf(dev->name, sizeof(dev->name), "%s", name);
+ spin_lock_init(&dev->lock);
+
+ for (i=0; i<3; ++i) {
+ unsigned long addr, size;
+
+ addr = pci_resource_start(pdev, i);
+ size = pci_resource_len(pdev, i);
+
+ dev->bar[i] = ioremap_nocache(addr, size);
+ if (!dev->bar[i])
+ goto err_out_unmap_bars;
+ }
+
+ dev->result_mem = __get_free_pages(GFP_KERNEL, HIFN_MAX_RESULT_ORDER);
+ if (!dev->result_mem) {
+ dprintk("Failed to allocate %d pages for result_mem.\n",
+ HIFN_MAX_RESULT_ORDER);
+ goto err_out_unmap_bars;
+ }
+ memset((void *)dev->result_mem, 0, PAGE_SIZE*(1<<HIFN_MAX_RESULT_ORDER));
+
+ dev->dst = pci_map_single(pdev, (void *)dev->result_mem,
+ PAGE_SIZE << HIFN_MAX_RESULT_ORDER, PCI_DMA_FROMDEVICE);
+
+ dev->desc_virt = pci_alloc_consistent(pdev, sizeof(struct hifn_dma),
+ &dev->desc_dma);
+ if (!dev->desc_virt) {
+ dprintk("Failed to allocate descriptor rings.\n");
+ goto err_out_free_result_pages;
+ }
+ memset(dev->desc_virt, 0, sizeof(struct hifn_dma));
+
+ dev->pdev = pdev;
+ dev->irq = pdev->irq;
+
+ for (i=0; i<HIFN_D_RES_RSIZE; ++i)
+ dev->sa[i] = NULL;
+
+ pci_set_drvdata(pdev, dev);
+
+ tasklet_init(&dev->tasklet, hifn_tasklet_callback, (unsigned long)dev);
+
+ crypto_init_queue(&dev->queue, 1);
+
+ err = request_irq(dev->irq, hifn_interrupt, IRQF_SHARED, dev->name, dev);
+ if (err) {
+ dprintk("Failed to request IRQ%d: err: %d.\n", dev->irq, err);
+ dev->irq = 0;
+ goto err_out_free_desc;
+ }
+
+ err = hifn_start_device(dev);
+ if (err)
+ goto err_out_free_irq;
+
+ err = hifn_test(dev, 1, 0);
+ if (err)
+ goto err_out_stop_device;
+
+ err = hifn_register_rng(dev);
+ if (err)
+ goto err_out_stop_device;
+
+ err = hifn_register_alg(dev);
+ if (err)
+ goto err_out_unregister_rng;
+
+ INIT_DELAYED_WORK(&dev->work, hifn_work);
+ schedule_delayed_work(&dev->work, HZ);
+
+ dprintk("HIFN crypto accelerator card at %s has been "
+ "successfully registered as %s.\n",
+ pci_name(pdev), dev->name);
+
+ return 0;
+
+err_out_unregister_rng:
+ hifn_unregister_rng(dev);
+err_out_stop_device:
+ hifn_reset_dma(dev, 1);
+ hifn_stop_device(dev);
+err_out_free_irq:
+ free_irq(dev->irq, dev->name);
+ tasklet_kill(&dev->tasklet);
+err_out_free_desc:
+ pci_free_consistent(pdev, sizeof(struct hifn_dma),
+ dev->desc_virt, dev->desc_dma);
+
+err_out_free_result_pages:
+ pci_unmap_single(pdev, dev->dst, PAGE_SIZE << HIFN_MAX_RESULT_ORDER,
+ PCI_DMA_FROMDEVICE);
+ free_pages(dev->result_mem, HIFN_MAX_RESULT_ORDER);
+
+err_out_unmap_bars:
+ for (i=0; i<3; ++i)
+ if (dev->bar[i])
+ iounmap(dev->bar[i]);
+
+err_out_free_regions:
+ pci_release_regions(pdev);
+
+err_out_disable_pci_device:
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static void hifn_remove(struct pci_dev *pdev)
+{
+ int i;
+ struct hifn_device *dev;
+
+ dev = pci_get_drvdata(pdev);
+
+ if (dev) {
+ cancel_delayed_work(&dev->work);
+ flush_scheduled_work();
+
+ hifn_unregister_rng(dev);
+ hifn_unregister_alg(dev);
+ hifn_reset_dma(dev, 1);
+ hifn_stop_device(dev);
+
+ free_irq(dev->irq, dev->name);
+ tasklet_kill(&dev->tasklet);
+
+ hifn_flush(dev);
+
+ pci_free_consistent(pdev, sizeof(struct hifn_dma),
+ dev->desc_virt, dev->desc_dma);
+ pci_unmap_single(pdev, dev->dst,
+ PAGE_SIZE << HIFN_MAX_RESULT_ORDER,
+ PCI_DMA_FROMDEVICE);
+ free_pages(dev->result_mem, HIFN_MAX_RESULT_ORDER);
+ for (i=0; i<3; ++i)
+ if (dev->bar[i])
+ iounmap(dev->bar[i]);
+
+ kfree(dev);
+ }
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+static struct pci_device_id hifn_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_HIFN, PCI_DEVICE_ID_HIFN_7955) },
+ { PCI_DEVICE(PCI_VENDOR_ID_HIFN, PCI_DEVICE_ID_HIFN_7956) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, hifn_pci_tbl);
+
+static struct pci_driver hifn_pci_driver = {
+ .name = "hifn795x",
+ .id_table = hifn_pci_tbl,
+ .probe = hifn_probe,
+ .remove = __devexit_p(hifn_remove),
+};
+
+static int __devinit hifn_init(void)
+{
+ unsigned int freq;
+ int err;
+
+ if (strncmp(hifn_pll_ref, "ext", 3) &&
+ strncmp(hifn_pll_ref, "pci", 3)) {
+ printk(KERN_ERR "hifn795x: invalid hifn_pll_ref clock, "
+ "must be pci or ext");
+ return -EINVAL;
+ }
+
+ /*
+ * For the 7955/7956 the reference clock frequency must be in the
+ * range of 20MHz-100MHz. For the 7954 the upper bound is 66.67MHz,
+ * but this chip is currently not supported.
+ */
+ if (hifn_pll_ref[3] != '\0') {
+ freq = simple_strtoul(hifn_pll_ref + 3, NULL, 10);
+ if (freq < 20 || freq > 100) {
+ printk(KERN_ERR "hifn795x: invalid hifn_pll_ref "
+ "frequency, must be in the range "
+ "of 20-100");
+ return -EINVAL;
+ }
+ }
+
+ err = pci_register_driver(&hifn_pci_driver);
+ if (err < 0) {
+ dprintk("Failed to register PCI driver for %s device.\n",
+ hifn_pci_driver.name);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "Driver for HIFN 795x crypto accelerator chip "
+ "has been successfully registered.\n");
+
+ return 0;
+}
+
+static void __devexit hifn_fini(void)
+{
+ pci_unregister_driver(&hifn_pci_driver);
+
+ printk(KERN_INFO "Driver for HIFN 795x crypto accelerator chip "
+ "has been successfully unregistered.\n");
+}
+
+module_init(hifn_init);
+module_exit(hifn_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for HIFN 795x crypto accelerator chip.");
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index 5f7e71810489..2f3ad3f7dfea 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -44,6 +44,7 @@
*/
#include <crypto/algapi.h>
+#include <crypto/aes.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -53,9 +54,6 @@
#include <asm/byteorder.h>
#include "padlock.h"
-#define AES_MIN_KEY_SIZE 16 /* in uint8_t units */
-#define AES_MAX_KEY_SIZE 32 /* ditto */
-#define AES_BLOCK_SIZE 16 /* ditto */
#define AES_EXTENDED_KEY_SIZE 64 /* in uint32_t units */
#define AES_EXTENDED_KEY_SIZE_B (AES_EXTENDED_KEY_SIZE * sizeof(uint32_t))
@@ -419,6 +417,11 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
/* ====== Encryption/decryption routines ====== */
/* These are the real call to PadLock. */
+static inline void padlock_reset_key(void)
+{
+ asm volatile ("pushfl; popfl");
+}
+
static inline void padlock_xcrypt(const u8 *input, u8 *output, void *key,
void *control_word)
{
@@ -439,8 +442,6 @@ static void aes_crypt_copy(const u8 *in, u8 *out, u32 *key, struct cword *cword)
static inline void aes_crypt(const u8 *in, u8 *out, u32 *key,
struct cword *cword)
{
- asm volatile ("pushfl; popfl");
-
/* padlock_xcrypt requires at least two blocks of data. */
if (unlikely(!(((unsigned long)in ^ (PAGE_SIZE - AES_BLOCK_SIZE)) &
(PAGE_SIZE - 1)))) {
@@ -459,7 +460,6 @@ static inline void padlock_xcrypt_ecb(const u8 *input, u8 *output, void *key,
return;
}
- asm volatile ("pushfl; popfl"); /* enforce key reload. */
asm volatile ("test $1, %%cl;"
"je 1f;"
"lea -1(%%ecx), %%eax;"
@@ -476,8 +476,6 @@ static inline void padlock_xcrypt_ecb(const u8 *input, u8 *output, void *key,
static inline u8 *padlock_xcrypt_cbc(const u8 *input, u8 *output, void *key,
u8 *iv, void *control_word, u32 count)
{
- /* Enforce key reload. */
- asm volatile ("pushfl; popfl");
/* rep xcryptcbc */
asm volatile (".byte 0xf3,0x0f,0xa7,0xd0"
: "+S" (input), "+D" (output), "+a" (iv)
@@ -488,12 +486,14 @@ static inline u8 *padlock_xcrypt_cbc(const u8 *input, u8 *output, void *key,
static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
struct aes_ctx *ctx = aes_ctx(tfm);
+ padlock_reset_key();
aes_crypt(in, out, ctx->E, &ctx->cword.encrypt);
}
static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
struct aes_ctx *ctx = aes_ctx(tfm);
+ padlock_reset_key();
aes_crypt(in, out, ctx->D, &ctx->cword.decrypt);
}
@@ -526,6 +526,8 @@ static int ecb_aes_encrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err;
+ padlock_reset_key();
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -548,6 +550,8 @@ static int ecb_aes_decrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err;
+ padlock_reset_key();
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -592,6 +596,8 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err;
+ padlock_reset_key();
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -616,6 +622,8 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
struct blkcipher_walk walk;
int err;
+ padlock_reset_key();
+
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index d59b2f417306..bcf52df30339 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -41,12 +41,12 @@
* the definition of dma_event_callback in dmaengine.h.
*
* Each device has a kref, which is initialized to 1 when the device is
- * registered. A kref_get is done for each class_device registered. When the
- * class_device is released, the coresponding kref_put is done in the release
+ * registered. A kref_get is done for each device registered. When the
+ * device is released, the coresponding kref_put is done in the release
* method. Every time one of the device's channels is allocated to a client,
* a kref_get occurs. When the channel is freed, the coresponding kref_put
* happens. The device's release function does a completion, so
- * unregister_device does a remove event, class_device_unregister, a kref_put
+ * unregister_device does a remove event, device_unregister, a kref_put
* for the first reference, then waits on the completion for all other
* references to finish.
*
@@ -77,9 +77,9 @@ static LIST_HEAD(dma_client_list);
/* --- sysfs implementation --- */
-static ssize_t show_memcpy_count(struct class_device *cd, char *buf)
+static ssize_t show_memcpy_count(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev);
+ struct dma_chan *chan = to_dma_chan(dev);
unsigned long count = 0;
int i;
@@ -89,9 +89,10 @@ static ssize_t show_memcpy_count(struct class_device *cd, char *buf)
return sprintf(buf, "%lu\n", count);
}
-static ssize_t show_bytes_transferred(struct class_device *cd, char *buf)
+static ssize_t show_bytes_transferred(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev);
+ struct dma_chan *chan = to_dma_chan(dev);
unsigned long count = 0;
int i;
@@ -101,9 +102,9 @@ static ssize_t show_bytes_transferred(struct class_device *cd, char *buf)
return sprintf(buf, "%lu\n", count);
}
-static ssize_t show_in_use(struct class_device *cd, char *buf)
+static ssize_t show_in_use(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev);
+ struct dma_chan *chan = to_dma_chan(dev);
int in_use = 0;
if (unlikely(chan->slow_ref) &&
@@ -119,7 +120,7 @@ static ssize_t show_in_use(struct class_device *cd, char *buf)
return sprintf(buf, "%d\n", in_use);
}
-static struct class_device_attribute dma_class_attrs[] = {
+static struct device_attribute dma_attrs[] = {
__ATTR(memcpy_count, S_IRUGO, show_memcpy_count, NULL),
__ATTR(bytes_transferred, S_IRUGO, show_bytes_transferred, NULL),
__ATTR(in_use, S_IRUGO, show_in_use, NULL),
@@ -128,16 +129,16 @@ static struct class_device_attribute dma_class_attrs[] = {
static void dma_async_device_cleanup(struct kref *kref);
-static void dma_class_dev_release(struct class_device *cd)
+static void dma_dev_release(struct device *dev)
{
- struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev);
+ struct dma_chan *chan = to_dma_chan(dev);
kref_put(&chan->device->refcount, dma_async_device_cleanup);
}
static struct class dma_devclass = {
- .name = "dma",
- .class_dev_attrs = dma_class_attrs,
- .release = dma_class_dev_release,
+ .name = "dma",
+ .dev_attrs = dma_attrs,
+ .dev_release = dma_dev_release,
};
/* --- client and device registration --- */
@@ -377,12 +378,12 @@ int dma_async_device_register(struct dma_device *device)
continue;
chan->chan_id = chancnt++;
- chan->class_dev.class = &dma_devclass;
- chan->class_dev.dev = NULL;
- snprintf(chan->class_dev.class_id, BUS_ID_SIZE, "dma%dchan%d",
+ chan->dev.class = &dma_devclass;
+ chan->dev.parent = NULL;
+ snprintf(chan->dev.bus_id, BUS_ID_SIZE, "dma%dchan%d",
device->dev_id, chan->chan_id);
- rc = class_device_register(&chan->class_dev);
+ rc = device_register(&chan->dev);
if (rc) {
chancnt--;
free_percpu(chan->local);
@@ -411,7 +412,7 @@ err_out:
if (chan->local == NULL)
continue;
kref_put(&device->refcount, dma_async_device_cleanup);
- class_device_unregister(&chan->class_dev);
+ device_unregister(&chan->dev);
chancnt--;
free_percpu(chan->local);
}
@@ -445,7 +446,7 @@ void dma_async_device_unregister(struct dma_device *device)
list_for_each_entry(chan, &device->channels, device_node) {
dma_clients_notify_removed(chan);
- class_device_unregister(&chan->class_dev);
+ device_unregister(&chan->dev);
dma_chan_release(chan);
}
diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c
index 70b837f23c43..53764577035f 100644
--- a/drivers/edac/edac_device_sysfs.c
+++ b/drivers/edac/edac_device_sysfs.c
@@ -246,16 +246,6 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
/* Init the devices's kobject */
memset(&edac_dev->kobj, 0, sizeof(struct kobject));
- edac_dev->kobj.ktype = &ktype_device_ctrl;
-
- /* set this new device under the edac_class kobject */
- edac_dev->kobj.parent = &edac_class->kset.kobj;
-
- /* generate sysfs "..../edac/<name>" */
- debugf4("%s() set name of kobject to: %s\n", __func__, edac_dev->name);
- err = kobject_set_name(&edac_dev->kobj, "%s", edac_dev->name);
- if (err)
- goto err_out;
/* Record which module 'owns' this control structure
* and bump the ref count of the module
@@ -268,12 +258,15 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
}
/* register */
- err = kobject_register(&edac_dev->kobj);
+ err = kobject_init_and_add(&edac_dev->kobj, &ktype_device_ctrl,
+ &edac_class->kset.kobj,
+ "%s", edac_dev->name);
if (err) {
debugf1("%s()Failed to register '.../edac/%s'\n",
__func__, edac_dev->name);
goto err_kobj_reg;
}
+ kobject_uevent(&edac_dev->kobj, KOBJ_ADD);
/* At this point, to 'free' the control struct,
* edac_device_unregister_sysfs_main_kobj() must be used
@@ -310,7 +303,7 @@ void edac_device_unregister_sysfs_main_kobj(
* a) module_put() this module
* b) 'kfree' the memory
*/
- kobject_unregister(&edac_dev->kobj);
+ kobject_put(&edac_dev->kobj);
}
/* edac_dev -> instance information */
@@ -533,12 +526,6 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
/* init this block's kobject */
memset(&block->kobj, 0, sizeof(struct kobject));
- block->kobj.parent = &instance->kobj;
- block->kobj.ktype = &ktype_block_ctrl;
-
- err = kobject_set_name(&block->kobj, "%s", block->name);
- if (err)
- return err;
/* bump the main kobject's reference count for this controller
* and this instance is dependant on the main
@@ -550,7 +537,9 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
}
/* Add this block's kobject */
- err = kobject_register(&block->kobj);
+ err = kobject_init_and_add(&block->kobj, &ktype_block_ctrl,
+ &instance->kobj,
+ "%s", block->name);
if (err) {
debugf1("%s() Failed to register instance '%s'\n",
__func__, block->name);
@@ -579,12 +568,13 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
goto err_on_attrib;
}
}
+ kobject_uevent(&block->kobj, KOBJ_ADD);
return 0;
/* Error unwind stack */
err_on_attrib:
- kobject_unregister(&block->kobj);
+ kobject_put(&block->kobj);
err_out:
return err;
@@ -615,7 +605,7 @@ static void edac_device_delete_block(struct edac_device_ctl_info *edac_dev,
/* unregister this block's kobject, SEE:
* edac_device_ctrl_block_release() callback operation
*/
- kobject_unregister(&block->kobj);
+ kobject_put(&block->kobj);
}
/* instance ctor/dtor code */
@@ -637,15 +627,8 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
/* Init the instance's kobject */
memset(&instance->kobj, 0, sizeof(struct kobject));
- /* set this new device under the edac_device main kobject */
- instance->kobj.parent = &edac_dev->kobj;
- instance->kobj.ktype = &ktype_instance_ctrl;
instance->ctl = edac_dev;
- err = kobject_set_name(&instance->kobj, "%s", instance->name);
- if (err)
- goto err_out;
-
/* bump the main kobject's reference count for this controller
* and this instance is dependant on the main
*/
@@ -655,8 +638,9 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
goto err_out;
}
- /* Formally register this instance's kobject */
- err = kobject_register(&instance->kobj);
+ /* Formally register this instance's kobject under the edac_device */
+ err = kobject_init_and_add(&instance->kobj, &ktype_instance_ctrl,
+ &edac_dev->kobj, "%s", instance->name);
if (err != 0) {
debugf2("%s() Failed to register instance '%s'\n",
__func__, instance->name);
@@ -679,6 +663,7 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
goto err_release_instance_kobj;
}
}
+ kobject_uevent(&instance->kobj, KOBJ_ADD);
debugf4("%s() Registered instance %d '%s' kobject\n",
__func__, idx, instance->name);
@@ -687,7 +672,7 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
/* error unwind stack */
err_release_instance_kobj:
- kobject_unregister(&instance->kobj);
+ kobject_put(&instance->kobj);
err_out:
return err;
@@ -712,7 +697,7 @@ static void edac_device_delete_instance(struct edac_device_ctl_info *edac_dev,
/* unregister this instance's kobject, SEE:
* edac_device_ctrl_instance_release() for callback operation
*/
- kobject_unregister(&instance->kobj);
+ kobject_put(&instance->kobj);
}
/*
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 3706b2bc0987..9aac88027fb3 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -380,13 +380,6 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci,
/* generate ..../edac/mc/mc<id>/csrow<index> */
memset(&csrow->kobj, 0, sizeof(csrow->kobj));
csrow->mci = mci; /* include container up link */
- csrow->kobj.parent = kobj_mci;
- csrow->kobj.ktype = &ktype_csrow;
-
- /* name this instance of csrow<id> */
- err = kobject_set_name(&csrow->kobj, "csrow%d", index);
- if (err)
- goto err_out;
/* bump the mci instance's kobject's ref count */
kobj = kobject_get(&mci->edac_mci_kobj);
@@ -396,12 +389,13 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci,
}
/* Instanstiate the csrow object */
- err = kobject_register(&csrow->kobj);
+ err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci,
+ "csrow%d", index);
if (err)
goto err_release_top_kobj;
/* At this point, to release a csrow kobj, one must
- * call the kobject_unregister and allow that tear down
+ * call the kobject_put and allow that tear down
* to work the releasing
*/
@@ -412,11 +406,11 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci,
err = edac_create_channel_files(&csrow->kobj, chan);
if (err) {
/* special case the unregister here */
- kobject_unregister(&csrow->kobj);
+ kobject_put(&csrow->kobj);
goto err_out;
}
}
-
+ kobject_uevent(&csrow->kobj, KOBJ_ADD);
return 0;
/* error unwind stack */
@@ -744,7 +738,6 @@ static struct kobj_type ktype_mc_set_attribs = {
*/
static struct kset mc_kset = {
.kobj = {.ktype = &ktype_mc_set_attribs },
- .ktype = &ktype_mci,
};
@@ -765,14 +758,6 @@ int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci)
/* Init the mci's kobject */
memset(kobj_mci, 0, sizeof(*kobj_mci));
- /* this instance become part of the mc_kset */
- kobj_mci->kset = &mc_kset;
-
- /* set the name of the mc<id> object */
- err = kobject_set_name(kobj_mci, "mc%d", mci->mc_idx);
- if (err)
- goto fail_out;
-
/* Record which module 'owns' this control structure
* and bump the ref count of the module
*/
@@ -784,13 +769,18 @@ int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci)
goto fail_out;
}
+ /* this instance become part of the mc_kset */
+ kobj_mci->kset = &mc_kset;
+
/* register the mc<id> kobject to the mc_kset */
- err = kobject_register(kobj_mci);
+ err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL,
+ "mc%d", mci->mc_idx);
if (err) {
debugf1("%s()Failed to register '.../edac/mc%d'\n",
__func__, mci->mc_idx);
goto kobj_reg_fail;
}
+ kobject_uevent(kobj_mci, KOBJ_ADD);
/* At this point, to 'free' the control struct,
* edac_mc_unregister_sysfs_main_kobj() must be used
@@ -818,7 +808,7 @@ fail_out:
void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
{
/* delete the kobj from the mc_kset */
- kobject_unregister(&mci->edac_mci_kobj);
+ kobject_put(&mci->edac_mci_kobj);
}
#define EDAC_DEVICE_SYMLINK "device"
@@ -933,7 +923,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
fail1:
for (i--; i >= 0; i--) {
if (csrow->nr_pages > 0) {
- kobject_unregister(&mci->csrows[i].kobj);
+ kobject_put(&mci->csrows[i].kobj);
}
}
@@ -960,7 +950,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
for (i = 0; i < mci->nr_csrows; i++) {
if (mci->csrows[i].nr_pages > 0) {
debugf0("%s() unreg csrow-%d\n", __func__, i);
- kobject_unregister(&mci->csrows[i].kobj);
+ kobject_put(&mci->csrows[i].kobj);
}
}
@@ -977,7 +967,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
debugf0("%s() unregister this mci kobj\n", __func__);
/* unregister this instance's kobject */
- kobject_unregister(&mci->edac_mci_kobj);
+ kobject_put(&mci->edac_mci_kobj);
}
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c
index e0c4a4086055..7e1374afd967 100644
--- a/drivers/edac/edac_module.c
+++ b/drivers/edac/edac_module.c
@@ -31,7 +31,7 @@ struct workqueue_struct *edac_workqueue;
* need to export to other files in this modules
*/
static struct sysdev_class edac_class = {
- set_kset_name("edac"),
+ .name = "edac",
};
static int edac_class_valid;
diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c
index 69f5dddabddf..5b075da99145 100644
--- a/drivers/edac/edac_pci_sysfs.c
+++ b/drivers/edac/edac_pci_sysfs.c
@@ -162,14 +162,6 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
debugf0("%s()\n", __func__);
- /* Set the parent and the instance's ktype */
- pci->kobj.parent = &edac_pci_top_main_kobj;
- pci->kobj.ktype = &ktype_pci_instance;
-
- err = kobject_set_name(&pci->kobj, "pci%d", idx);
- if (err)
- return err;
-
/* First bump the ref count on the top main kobj, which will
* track the number of PCI instances we have, and thus nest
* properly on keeping the module loaded
@@ -181,7 +173,8 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
}
/* And now register this new kobject under the main kobj */
- err = kobject_register(&pci->kobj);
+ err = kobject_init_and_add(&pci->kobj, &ktype_pci_instance,
+ &edac_pci_top_main_kobj, "pci%d", idx);
if (err != 0) {
debugf2("%s() failed to register instance pci%d\n",
__func__, idx);
@@ -189,6 +182,7 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
goto error_out;
}
+ kobject_uevent(&pci->kobj, KOBJ_ADD);
debugf1("%s() Register instance 'pci%d' kobject\n", __func__, idx);
return 0;
@@ -211,7 +205,7 @@ void edac_pci_unregister_sysfs_instance_kobj(struct edac_pci_ctl_info *pci)
* function release the main reference count and then
* kfree the memory
*/
- kobject_unregister(&pci->kobj);
+ kobject_put(&pci->kobj);
}
/***************************** EDAC PCI sysfs root **********************/
@@ -364,14 +358,6 @@ int edac_pci_main_kobj_setup(void)
goto decrement_count_fail;
}
- /* Need the kobject hook ups, and name setting */
- edac_pci_top_main_kobj.ktype = &ktype_edac_pci_main_kobj;
- edac_pci_top_main_kobj.parent = &edac_class->kset.kobj;
-
- err = kobject_set_name(&edac_pci_top_main_kobj, "pci");
- if (err)
- goto decrement_count_fail;
-
/* Bump the reference count on this module to ensure the
* modules isn't unloaded until we deconstruct the top
* level main kobj for EDAC PCI
@@ -383,23 +369,24 @@ int edac_pci_main_kobj_setup(void)
}
/* Instanstiate the pci object */
- /* FIXME: maybe new sysdev_create_subdir() */
- err = kobject_register(&edac_pci_top_main_kobj);
+ err = kobject_init_and_add(&edac_pci_top_main_kobj, &ktype_edac_pci_main_kobj,
+ &edac_class->kset.kobj, "pci");
if (err) {
debugf1("Failed to register '.../edac/pci'\n");
- goto kobject_register_fail;
+ goto kobject_init_and_add_fail;
}
/* At this point, to 'release' the top level kobject
* for EDAC PCI, then edac_pci_main_kobj_teardown()
* must be used, for resources to be cleaned up properly
*/
+ kobject_uevent(&edac_pci_top_main_kobj, KOBJ_ADD);
debugf1("Registered '.../edac/pci' kobject\n");
return 0;
/* Error unwind statck */
-kobject_register_fail:
+kobject_init_and_add_fail:
module_put(THIS_MODULE);
decrement_count_fail:
@@ -424,9 +411,9 @@ static void edac_pci_main_kobj_teardown(void)
* main kobj
*/
if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) {
- debugf0("%s() called kobject_unregister on main kobj\n",
+ debugf0("%s() called kobject_put on main kobj\n",
__func__);
- kobject_unregister(&edac_pci_top_main_kobj);
+ kobject_put(&edac_pci_top_main_kobj);
}
}
diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c
index 6942e065e609..d168223db159 100644
--- a/drivers/firmware/edd.c
+++ b/drivers/firmware/edd.c
@@ -631,7 +631,7 @@ static struct kobj_type edd_ktype = {
.default_attrs = def_attrs,
};
-static decl_subsys(edd, &edd_ktype, NULL);
+static struct kset *edd_kset;
/**
@@ -693,7 +693,7 @@ edd_create_symlink_to_pcidev(struct edd_device *edev)
static inline void
edd_device_unregister(struct edd_device *edev)
{
- kobject_unregister(&edev->kobj);
+ kobject_put(&edev->kobj);
}
static void edd_populate_dir(struct edd_device * edev)
@@ -721,12 +721,13 @@ edd_device_register(struct edd_device *edev, int i)
if (!edev)
return 1;
edd_dev_set_info(edev, i);
- kobject_set_name(&edev->kobj, "int13_dev%02x",
- 0x80 + i);
- kobj_set_kset_s(edev,edd_subsys);
- error = kobject_register(&edev->kobj);
- if (!error)
+ edev->kobj.kset = edd_kset;
+ error = kobject_init_and_add(&edev->kobj, &edd_ktype, NULL,
+ "int13_dev%02x", 0x80 + i);
+ if (!error) {
edd_populate_dir(edev);
+ kobject_uevent(&edev->kobj, KOBJ_ADD);
+ }
return error;
}
@@ -755,9 +756,9 @@ edd_init(void)
return 1;
}
- rc = firmware_register(&edd_subsys);
- if (rc)
- return rc;
+ edd_kset = kset_create_and_add("edd", NULL, firmware_kobj);
+ if (!edd_kset)
+ return -ENOMEM;
for (i = 0; i < edd_num_devices() && !rc; i++) {
edev = kzalloc(sizeof (*edev), GFP_KERNEL);
@@ -773,7 +774,7 @@ edd_init(void)
}
if (rc)
- firmware_unregister(&edd_subsys);
+ kset_unregister(edd_kset);
return rc;
}
@@ -787,7 +788,7 @@ edd_exit(void)
if ((edev = edd_devices[i]))
edd_device_unregister(edev);
}
- firmware_unregister(&edd_subsys);
+ kset_unregister(edd_kset);
}
late_initcall(edd_init);
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 858a7b95933b..f4f709d1370b 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -129,13 +129,6 @@ struct efivar_attribute {
};
-#define EFI_ATTR(_name, _mode, _show, _store) \
-struct subsys_attribute efi_attr_##_name = { \
- .attr = {.name = __stringify(_name), .mode = _mode}, \
- .show = _show, \
- .store = _store, \
-};
-
#define EFIVAR_ATTR(_name, _mode, _show, _store) \
struct efivar_attribute efivar_attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode}, \
@@ -143,13 +136,6 @@ struct efivar_attribute efivar_attr_##_name = { \
.store = _store, \
};
-#define VAR_SUBSYS_ATTR(_name, _mode, _show, _store) \
-struct subsys_attribute var_subsys_attr_##_name = { \
- .attr = {.name = __stringify(_name), .mode = _mode}, \
- .show = _show, \
- .store = _store, \
-};
-
#define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr)
#define to_efivar_entry(obj) container_of(obj, struct efivar_entry, kobj)
@@ -408,21 +394,16 @@ static struct kobj_type efivar_ktype = {
.default_attrs = def_attrs,
};
-static ssize_t
-dummy(struct kset *kset, char *buf)
-{
- return -ENODEV;
-}
-
static inline void
efivar_unregister(struct efivar_entry *var)
{
- kobject_unregister(&var->kobj);
+ kobject_put(&var->kobj);
}
-static ssize_t
-efivar_create(struct kset *kset, const char *buf, size_t count)
+static ssize_t efivar_create(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t count)
{
struct efi_variable *new_var = (struct efi_variable *)buf;
struct efivar_entry *search_efivar, *n;
@@ -479,8 +460,9 @@ efivar_create(struct kset *kset, const char *buf, size_t count)
return count;
}
-static ssize_t
-efivar_delete(struct kset *kset, const char *buf, size_t count)
+static ssize_t efivar_delete(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t count)
{
struct efi_variable *del_var = (struct efi_variable *)buf;
struct efivar_entry *search_efivar, *n;
@@ -537,25 +519,26 @@ efivar_delete(struct kset *kset, const char *buf, size_t count)
return count;
}
-static VAR_SUBSYS_ATTR(new_var, 0200, dummy, efivar_create);
-static VAR_SUBSYS_ATTR(del_var, 0200, dummy, efivar_delete);
+static struct bin_attribute var_subsys_attr_new_var = {
+ .attr = {.name = "new_var", .mode = 0200},
+ .write = efivar_create,
+};
-static struct subsys_attribute *var_subsys_attrs[] = {
- &var_subsys_attr_new_var,
- &var_subsys_attr_del_var,
- NULL,
+static struct bin_attribute var_subsys_attr_del_var = {
+ .attr = {.name = "del_var", .mode = 0200},
+ .write = efivar_delete,
};
/*
* Let's not leave out systab information that snuck into
* the efivars driver
*/
-static ssize_t
-systab_read(struct kset *kset, char *buf)
+static ssize_t systab_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
char *str = buf;
- if (!kset || !buf)
+ if (!kobj || !buf)
return -EINVAL;
if (efi.mps != EFI_INVALID_TABLE_ADDR)
@@ -576,15 +559,21 @@ systab_read(struct kset *kset, char *buf)
return str - buf;
}
-static EFI_ATTR(systab, 0400, systab_read, NULL);
+static struct kobj_attribute efi_attr_systab =
+ __ATTR(systab, 0400, systab_show, NULL);
-static struct subsys_attribute *efi_subsys_attrs[] = {
- &efi_attr_systab,
+static struct attribute *efi_subsys_attrs[] = {
+ &efi_attr_systab.attr,
NULL, /* maybe more in the future? */
};
-static decl_subsys(vars, &efivar_ktype, NULL);
-static decl_subsys(efi, NULL, NULL);
+static struct attribute_group efi_subsys_attr_group = {
+ .attrs = efi_subsys_attrs,
+};
+
+
+static struct kset *vars_kset;
+static struct kobject *efi_kobj;
/*
* efivar_create_sysfs_entry()
@@ -628,15 +617,16 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
*(short_name + strlen(short_name)) = '-';
efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
- kobject_set_name(&new_efivar->kobj, "%s", short_name);
- kobj_set_kset_s(new_efivar, vars_subsys);
- i = kobject_register(&new_efivar->kobj);
+ new_efivar->kobj.kset = vars_kset;
+ i = kobject_init_and_add(&new_efivar->kobj, &efivar_ktype, NULL,
+ "%s", short_name);
if (i) {
kfree(short_name);
kfree(new_efivar);
return 1;
}
+ kobject_uevent(&new_efivar->kobj, KOBJ_ADD);
kfree(short_name);
short_name = NULL;
@@ -660,9 +650,8 @@ efivars_init(void)
efi_status_t status = EFI_NOT_FOUND;
efi_guid_t vendor_guid;
efi_char16_t *variable_name;
- struct subsys_attribute *attr;
unsigned long variable_name_size = 1024;
- int i, error = 0;
+ int error = 0;
if (!efi_enabled)
return -ENODEV;
@@ -676,23 +665,18 @@ efivars_init(void)
printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
EFIVARS_DATE);
- /*
- * For now we'll register the efi subsys within this driver
- */
-
- error = firmware_register(&efi_subsys);
-
- if (error) {
- printk(KERN_ERR "efivars: Firmware registration failed with error %d.\n", error);
+ /* For now we'll register the efi directory at /sys/firmware/efi */
+ efi_kobj = kobject_create_and_add("efi", firmware_kobj);
+ if (!efi_kobj) {
+ printk(KERN_ERR "efivars: Firmware registration failed.\n");
+ error = -ENOMEM;
goto out_free;
}
- kobj_set_kset_s(&vars_subsys, efi_subsys);
-
- error = subsystem_register(&vars_subsys);
-
- if (error) {
- printk(KERN_ERR "efivars: Subsystem registration failed with error %d.\n", error);
+ vars_kset = kset_create_and_add("vars", NULL, efi_kobj);
+ if (!vars_kset) {
+ printk(KERN_ERR "efivars: Subsystem registration failed.\n");
+ error = -ENOMEM;
goto out_firmware_unregister;
}
@@ -727,28 +711,28 @@ efivars_init(void)
* Now add attributes to allow creation of new vars
* and deletion of existing ones...
*/
-
- for (i = 0; (attr = var_subsys_attrs[i]) && !error; i++) {
- if (attr->show && attr->store)
- error = subsys_create_file(&vars_subsys, attr);
- }
+ error = sysfs_create_bin_file(&vars_kset->kobj,
+ &var_subsys_attr_new_var);
+ if (error)
+ printk(KERN_ERR "efivars: unable to create new_var sysfs file"
+ " due to error %d\n", error);
+ error = sysfs_create_bin_file(&vars_kset->kobj,
+ &var_subsys_attr_del_var);
+ if (error)
+ printk(KERN_ERR "efivars: unable to create del_var sysfs file"
+ " due to error %d\n", error);
/* Don't forget the systab entry */
-
- for (i = 0; (attr = efi_subsys_attrs[i]) && !error; i++) {
- if (attr->show)
- error = subsys_create_file(&efi_subsys, attr);
- }
-
+ error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
if (error)
printk(KERN_ERR "efivars: Sysfs attribute export failed with error %d.\n", error);
else
goto out_free;
- subsystem_unregister(&vars_subsys);
+ kset_unregister(vars_kset);
out_firmware_unregister:
- firmware_unregister(&efi_subsys);
+ kobject_put(efi_kobj);
out_free:
kfree(variable_name);
@@ -768,8 +752,8 @@ efivars_exit(void)
efivar_unregister(entry);
}
- subsystem_unregister(&vars_subsys);
- firmware_unregister(&efi_subsys);
+ kset_unregister(vars_kset);
+ kobject_put(efi_kobj);
}
module_init(efivars_init);
diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c
index b767603a07ba..ebfbb2947ae6 100644
--- a/drivers/i2c/chips/isp1301_omap.c
+++ b/drivers/i2c/chips/isp1301_omap.c
@@ -259,12 +259,6 @@ static inline const char *state_name(struct isp1301 *isp)
return state_string(isp->otg.state);
}
-#ifdef VERBOSE
-#define dev_vdbg dev_dbg
-#else
-#define dev_vdbg(dev, fmt, arg...) do{}while(0)
-#endif
-
/*-------------------------------------------------------------------------*/
/* NOTE: some of this ISP1301 setup is specific to H2 boards;
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 2994523be7bf..0cb3d2bb3ab9 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -1173,7 +1173,7 @@ static struct kobject *exact_match(dev_t dev, int *part, void *data)
{
struct gendisk *p = data;
*part &= (1 << PARTN_BITS) - 1;
- return &p->kobj;
+ return &p->dev.kobj;
}
static int exact_lock(dev_t dev, void *data)
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 7b9181b5469d..1495792d7917 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -4724,10 +4724,8 @@ static void ide_tape_release(struct kref *kref)
drive->dsc_overlap = 0;
drive->driver_data = NULL;
- class_device_destroy(idetape_sysfs_class,
- MKDEV(IDETAPE_MAJOR, tape->minor));
- class_device_destroy(idetape_sysfs_class,
- MKDEV(IDETAPE_MAJOR, tape->minor + 128));
+ device_destroy(idetape_sysfs_class, MKDEV(IDETAPE_MAJOR, tape->minor));
+ device_destroy(idetape_sysfs_class, MKDEV(IDETAPE_MAJOR, tape->minor + 128));
idetape_devs[tape->minor] = NULL;
g->private_data = NULL;
put_disk(g);
@@ -4884,10 +4882,10 @@ static int ide_tape_probe(ide_drive_t *drive)
idetape_setup(drive, tape, minor);
- class_device_create(idetape_sysfs_class, NULL,
- MKDEV(IDETAPE_MAJOR, minor), &drive->gendev, "%s", tape->name);
- class_device_create(idetape_sysfs_class, NULL,
- MKDEV(IDETAPE_MAJOR, minor + 128), &drive->gendev, "n%s", tape->name);
+ device_create(idetape_sysfs_class, &drive->gendev,
+ MKDEV(IDETAPE_MAJOR, minor), "%s", tape->name);
+ device_create(idetape_sysfs_class, &drive->gendev,
+ MKDEV(IDETAPE_MAJOR, minor + 128), "n%s", tape->name);
g->fops = &idetape_block_ops;
ide_register_region(g);
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index 90dc75be3418..511e4321c6b6 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -727,33 +727,31 @@ static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
static DEFINE_MUTEX(nodemgr_serialize_remove_uds);
+static int __match_ne(struct device *dev, void *data)
+{
+ struct unit_directory *ud;
+ struct node_entry *ne = (struct node_entry *)data;
+
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ return ud->ne == ne;
+}
+
static void nodemgr_remove_uds(struct node_entry *ne)
{
struct device *dev;
- struct unit_directory *tmp, *ud;
-
- /* Iteration over nodemgr_ud_class.devices has to be protected by
- * nodemgr_ud_class.sem, but device_unregister() will eventually
- * take nodemgr_ud_class.sem too. Therefore pick out one ud at a time,
- * release the semaphore, and then unregister the ud. Since this code
- * may be called from other contexts besides the knodemgrds, protect the
- * gap after release of the semaphore by nodemgr_serialize_remove_uds.
+ struct unit_directory *ud;
+
+ /* Use class_find device to iterate the devices. Since this code
+ * may be called from other contexts besides the knodemgrds,
+ * protect it by nodemgr_serialize_remove_uds.
*/
mutex_lock(&nodemgr_serialize_remove_uds);
for (;;) {
- ud = NULL;
- down(&nodemgr_ud_class.sem);
- list_for_each_entry(dev, &nodemgr_ud_class.devices, node) {
- tmp = container_of(dev, struct unit_directory,
- unit_dev);
- if (tmp->ne == ne) {
- ud = tmp;
- break;
- }
- }
- up(&nodemgr_ud_class.sem);
- if (ud == NULL)
+ dev = class_find_device(&nodemgr_ud_class, ne, __match_ne);
+ if (!dev)
break;
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ put_device(dev);
device_unregister(&ud->unit_dev);
device_unregister(&ud->device);
}
@@ -882,45 +880,66 @@ fail_alloc:
return NULL;
}
+static int __match_ne_guid(struct device *dev, void *data)
+{
+ struct node_entry *ne;
+ u64 *guid = (u64 *)data;
+
+ ne = container_of(dev, struct node_entry, node_dev);
+ return ne->guid == *guid;
+}
static struct node_entry *find_entry_by_guid(u64 guid)
{
struct device *dev;
- struct node_entry *ne, *ret_ne = NULL;
-
- down(&nodemgr_ne_class.sem);
- list_for_each_entry(dev, &nodemgr_ne_class.devices, node) {
- ne = container_of(dev, struct node_entry, node_dev);
+ struct node_entry *ne;
- if (ne->guid == guid) {
- ret_ne = ne;
- break;
- }
- }
- up(&nodemgr_ne_class.sem);
+ dev = class_find_device(&nodemgr_ne_class, &guid, __match_ne_guid);
+ if (!dev)
+ return NULL;
+ ne = container_of(dev, struct node_entry, node_dev);
+ put_device(dev);
- return ret_ne;
+ return ne;
}
+struct match_nodeid_param {
+ struct hpsb_host *host;
+ nodeid_t nodeid;
+};
+
+static int __match_ne_nodeid(struct device *dev, void *data)
+{
+ int found = 0;
+ struct node_entry *ne;
+ struct match_nodeid_param *param = (struct match_nodeid_param *)data;
+
+ if (!dev)
+ goto ret;
+ ne = container_of(dev, struct node_entry, node_dev);
+ if (ne->host == param->host && ne->nodeid == param->nodeid)
+ found = 1;
+ret:
+ return found;
+}
static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
nodeid_t nodeid)
{
struct device *dev;
- struct node_entry *ne, *ret_ne = NULL;
+ struct node_entry *ne;
+ struct match_nodeid_param param;
- down(&nodemgr_ne_class.sem);
- list_for_each_entry(dev, &nodemgr_ne_class.devices, node) {
- ne = container_of(dev, struct node_entry, node_dev);
+ param.host = host;
+ param.nodeid = nodeid;
- if (ne->host == host && ne->nodeid == nodeid) {
- ret_ne = ne;
- break;
- }
- }
- up(&nodemgr_ne_class.sem);
+ dev = class_find_device(&nodemgr_ne_class, &param, __match_ne_nodeid);
+ if (!dev)
+ return NULL;
+ ne = container_of(dev, struct node_entry, node_dev);
+ put_device(dev);
- return ret_ne;
+ return ne;
}
@@ -1370,107 +1389,109 @@ static void nodemgr_node_scan(struct host_info *hi, int generation)
}
}
-
-static void nodemgr_suspend_ne(struct node_entry *ne)
+static int __nodemgr_driver_suspend(struct device *dev, void *data)
{
- struct device *dev;
struct unit_directory *ud;
struct device_driver *drv;
+ struct node_entry *ne = (struct node_entry *)data;
int error;
- HPSB_DEBUG("Node suspended: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
- NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ if (ud->ne == ne) {
+ drv = get_driver(ud->device.driver);
+ if (drv) {
+ error = 1; /* release if suspend is not implemented */
+ if (drv->suspend) {
+ down(&ud->device.sem);
+ error = drv->suspend(&ud->device, PMSG_SUSPEND);
+ up(&ud->device.sem);
+ }
+ if (error)
+ device_release_driver(&ud->device);
+ put_driver(drv);
+ }
+ }
- ne->in_limbo = 1;
- WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo));
+ return 0;
+}
- down(&nodemgr_ud_class.sem);
- list_for_each_entry(dev, &nodemgr_ud_class.devices, node) {
- ud = container_of(dev, struct unit_directory, unit_dev);
- if (ud->ne != ne)
- continue;
+static int __nodemgr_driver_resume(struct device *dev, void *data)
+{
+ struct unit_directory *ud;
+ struct device_driver *drv;
+ struct node_entry *ne = (struct node_entry *)data;
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ if (ud->ne == ne) {
drv = get_driver(ud->device.driver);
- if (!drv)
- continue;
-
- error = 1; /* release if suspend is not implemented */
- if (drv->suspend) {
- down(&ud->device.sem);
- error = drv->suspend(&ud->device, PMSG_SUSPEND);
- up(&ud->device.sem);
+ if (drv) {
+ if (drv->resume) {
+ down(&ud->device.sem);
+ drv->resume(&ud->device);
+ up(&ud->device.sem);
+ }
+ put_driver(drv);
}
- if (error)
- device_release_driver(&ud->device);
- put_driver(drv);
}
- up(&nodemgr_ud_class.sem);
-}
+ return 0;
+}
-static void nodemgr_resume_ne(struct node_entry *ne)
+static void nodemgr_suspend_ne(struct node_entry *ne)
{
- struct device *dev;
- struct unit_directory *ud;
- struct device_driver *drv;
+ HPSB_DEBUG("Node suspended: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
+ NODE_BUS_ARGS(ne->host, ne->nodeid),
+ (unsigned long long)ne->guid);
- ne->in_limbo = 0;
- device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
+ ne->in_limbo = 1;
+ WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo));
- down(&nodemgr_ud_class.sem);
- list_for_each_entry(dev, &nodemgr_ud_class.devices, node) {
- ud = container_of(dev, struct unit_directory, unit_dev);
- if (ud->ne != ne)
- continue;
+ class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_suspend);
+}
- drv = get_driver(ud->device.driver);
- if (!drv)
- continue;
- if (drv->resume) {
- down(&ud->device.sem);
- drv->resume(&ud->device);
- up(&ud->device.sem);
- }
- put_driver(drv);
- }
- up(&nodemgr_ud_class.sem);
+static void nodemgr_resume_ne(struct node_entry *ne)
+{
+ ne->in_limbo = 0;
+ device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
+ class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_resume);
HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
}
-
-static void nodemgr_update_pdrv(struct node_entry *ne)
+static int __nodemgr_update_pdrv(struct device *dev, void *data)
{
- struct device *dev;
struct unit_directory *ud;
struct device_driver *drv;
struct hpsb_protocol_driver *pdrv;
+ struct node_entry *ne = (struct node_entry *)data;
int error;
- down(&nodemgr_ud_class.sem);
- list_for_each_entry(dev, &nodemgr_ud_class.devices, node) {
- ud = container_of(dev, struct unit_directory, unit_dev);
- if (ud->ne != ne)
- continue;
-
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ if (ud->ne == ne) {
drv = get_driver(ud->device.driver);
- if (!drv)
- continue;
-
- error = 0;
- pdrv = container_of(drv, struct hpsb_protocol_driver, driver);
- if (pdrv->update) {
- down(&ud->device.sem);
- error = pdrv->update(ud);
- up(&ud->device.sem);
+ if (drv) {
+ error = 0;
+ pdrv = container_of(drv, struct hpsb_protocol_driver,
+ driver);
+ if (pdrv->update) {
+ down(&ud->device.sem);
+ error = pdrv->update(ud);
+ up(&ud->device.sem);
+ }
+ if (error)
+ device_release_driver(&ud->device);
+ put_driver(drv);
}
- if (error)
- device_release_driver(&ud->device);
- put_driver(drv);
}
- up(&nodemgr_ud_class.sem);
+
+ return 0;
+}
+
+static void nodemgr_update_pdrv(struct node_entry *ne)
+{
+ class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_update_pdrv);
}
@@ -1529,13 +1550,31 @@ static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int ge
put_device(dev);
}
+struct probe_param {
+ struct host_info *hi;
+ int generation;
+};
+
+static int __nodemgr_node_probe(struct device *dev, void *data)
+{
+ struct probe_param *param = (struct probe_param *)data;
+ struct node_entry *ne;
+
+ ne = container_of(dev, struct node_entry, node_dev);
+ if (!ne->needs_probe)
+ nodemgr_probe_ne(param->hi, ne, param->generation);
+ if (ne->needs_probe)
+ nodemgr_probe_ne(param->hi, ne, param->generation);
+ return 0;
+}
static void nodemgr_node_probe(struct host_info *hi, int generation)
{
struct hpsb_host *host = hi->host;
- struct device *dev;
- struct node_entry *ne;
+ struct probe_param param;
+ param.hi = hi;
+ param.generation = generation;
/* Do some processing of the nodes we've probed. This pulls them
* into the sysfs layer if needed, and can result in processing of
* unit-directories, or just updating the node and it's
@@ -1545,19 +1584,7 @@ static void nodemgr_node_probe(struct host_info *hi, int generation)
* while probes are time-consuming. (Well, those probes need some
* improvement...) */
- down(&nodemgr_ne_class.sem);
- list_for_each_entry(dev, &nodemgr_ne_class.devices, node) {
- ne = container_of(dev, struct node_entry, node_dev);
- if (!ne->needs_probe)
- nodemgr_probe_ne(hi, ne, generation);
- }
- list_for_each_entry(dev, &nodemgr_ne_class.devices, node) {
- ne = container_of(dev, struct node_entry, node_dev);
- if (ne->needs_probe)
- nodemgr_probe_ne(hi, ne, generation);
- }
- up(&nodemgr_ne_class.sem);
-
+ class_for_each_device(&nodemgr_ne_class, &param, __nodemgr_node_probe);
/* If we had a bus reset while we were scanning the bus, it is
* possible that we did not probe all nodes. In that case, we
@@ -1757,6 +1784,22 @@ exit:
return 0;
}
+struct host_iter_param {
+ void *data;
+ int (*cb)(struct hpsb_host *, void *);
+};
+
+static int __nodemgr_for_each_host(struct device *dev, void *data)
+{
+ struct hpsb_host *host;
+ struct host_iter_param *hip = (struct host_iter_param *)data;
+ int error = 0;
+
+ host = container_of(dev, struct hpsb_host, host_dev);
+ error = hip->cb(host, hip->data);
+
+ return error;
+}
/**
* nodemgr_for_each_host - call a function for each IEEE 1394 host
* @data: an address to supply to the callback
@@ -1771,18 +1814,13 @@ exit:
*/
int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *))
{
- struct device *dev;
- struct hpsb_host *host;
- int error = 0;
-
- down(&hpsb_host_class.sem);
- list_for_each_entry(dev, &hpsb_host_class.devices, node) {
- host = container_of(dev, struct hpsb_host, host_dev);
+ struct host_iter_param hip;
+ int error;
- if ((error = cb(host, data)))
- break;
- }
- up(&hpsb_host_class.sem);
+ hip.cb = cb;
+ hip.data = data;
+ error = class_for_each_device(&hpsb_host_class, &hip,
+ __nodemgr_for_each_host);
return error;
}
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 3d4050681325..c864ef70fdf9 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -508,19 +508,10 @@ static int add_port(struct ib_device *device, int port_num)
p->ibdev = device;
p->port_num = port_num;
- p->kobj.ktype = &port_type;
- p->kobj.parent = kobject_get(&device->ports_parent);
- if (!p->kobj.parent) {
- ret = -EBUSY;
- goto err;
- }
-
- ret = kobject_set_name(&p->kobj, "%d", port_num);
- if (ret)
- goto err_put;
-
- ret = kobject_register(&p->kobj);
+ ret = kobject_init_and_add(&p->kobj, &port_type,
+ kobject_get(device->ports_parent),
+ "%d", port_num);
if (ret)
goto err_put;
@@ -549,6 +540,7 @@ static int add_port(struct ib_device *device, int port_num)
list_add_tail(&p->kobj.entry, &device->port_list);
+ kobject_uevent(&p->kobj, KOBJ_ADD);
return 0;
err_free_pkey:
@@ -570,9 +562,7 @@ err_remove_pma:
sysfs_remove_group(&p->kobj, &pma_group);
err_put:
- kobject_put(&device->ports_parent);
-
-err:
+ kobject_put(device->ports_parent);
kfree(p);
return ret;
}
@@ -694,16 +684,9 @@ int ib_device_register_sysfs(struct ib_device *device)
goto err_unregister;
}
- device->ports_parent.parent = kobject_get(&class_dev->kobj);
- if (!device->ports_parent.parent) {
- ret = -EBUSY;
- goto err_unregister;
- }
- ret = kobject_set_name(&device->ports_parent, "ports");
- if (ret)
- goto err_put;
- ret = kobject_register(&device->ports_parent);
- if (ret)
+ device->ports_parent = kobject_create_and_add("ports",
+ kobject_get(&class_dev->kobj));
+ if (!device->ports_parent)
goto err_put;
if (device->node_type == RDMA_NODE_IB_SWITCH) {
@@ -731,7 +714,7 @@ err_put:
sysfs_remove_group(p, &pma_group);
sysfs_remove_group(p, &port->pkey_group);
sysfs_remove_group(p, &port->gid_group);
- kobject_unregister(p);
+ kobject_put(p);
}
}
@@ -755,10 +738,10 @@ void ib_device_unregister_sysfs(struct ib_device *device)
sysfs_remove_group(p, &pma_group);
sysfs_remove_group(p, &port->pkey_group);
sysfs_remove_group(p, &port->gid_group);
- kobject_unregister(p);
+ kobject_put(p);
}
- kobject_unregister(&device->ports_parent);
+ kobject_put(device->ports_parent);
class_device_unregister(&device->class_dev);
}
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 6a56d86a2951..c9e32b46387f 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -590,6 +590,11 @@ static struct attribute_group ehca_drv_attr_grp = {
.attrs = ehca_drv_attrs
};
+static struct attribute_group *ehca_drv_attr_groups[] = {
+ &ehca_drv_attr_grp,
+ NULL,
+};
+
#define EHCA_RESOURCE_ATTR(name) \
static ssize_t ehca_show_##name(struct device *dev, \
struct device_attribute *attr, \
@@ -899,6 +904,9 @@ static struct of_platform_driver ehca_driver = {
.match_table = ehca_device_table,
.probe = ehca_probe,
.remove = ehca_remove,
+ .driver = {
+ .groups = ehca_drv_attr_groups,
+ },
};
void ehca_poll_eqs(unsigned long data)
@@ -957,10 +965,6 @@ int __init ehca_module_init(void)
goto module_init2;
}
- ret = sysfs_create_group(&ehca_driver.driver.kobj, &ehca_drv_attr_grp);
- if (ret) /* only complain; we can live without attributes */
- ehca_gen_err("Cannot create driver attributes ret=%d", ret);
-
if (ehca_poll_all_eqs != 1) {
ehca_gen_err("WARNING!!!");
ehca_gen_err("It is possible to lose interrupts.");
@@ -986,7 +990,6 @@ void __exit ehca_module_exit(void)
if (ehca_poll_all_eqs == 1)
del_timer_sync(&poll_eqs_timer);
- sysfs_remove_group(&ehca_driver.driver.kobj, &ehca_drv_attr_grp);
ibmebus_unregister_driver(&ehca_driver);
ehca_destroy_slab_caches();
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index 1f152ded1e3c..fc355981bbab 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -121,6 +121,9 @@ static struct pci_driver ipath_driver = {
.probe = ipath_init_one,
.remove = __devexit_p(ipath_remove_one),
.id_table = ipath_pci_tbl,
+ .driver = {
+ .groups = ipath_driver_attr_groups,
+ },
};
static void ipath_check_status(struct work_struct *work)
@@ -2217,25 +2220,15 @@ static int __init infinipath_init(void)
goto bail_unit;
}
- ret = ipath_driver_create_group(&ipath_driver.driver);
- if (ret < 0) {
- printk(KERN_ERR IPATH_DRV_NAME ": Unable to create driver "
- "sysfs entries: error %d\n", -ret);
- goto bail_pci;
- }
-
ret = ipath_init_ipathfs();
if (ret < 0) {
printk(KERN_ERR IPATH_DRV_NAME ": Unable to create "
"ipathfs: error %d\n", -ret);
- goto bail_group;
+ goto bail_pci;
}
goto bail;
-bail_group:
- ipath_driver_remove_group(&ipath_driver.driver);
-
bail_pci:
pci_unregister_driver(&ipath_driver);
@@ -2250,8 +2243,6 @@ static void __exit infinipath_cleanup(void)
{
ipath_exit_ipathfs();
- ipath_driver_remove_group(&ipath_driver.driver);
-
ipath_cdbg(VERBOSE, "Unregistering pci driver\n");
pci_unregister_driver(&ipath_driver);
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index 8786dd7922e4..bb1dc075f1d1 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -938,8 +938,7 @@ struct device_driver;
extern const char ib_ipath_version[];
-int ipath_driver_create_group(struct device_driver *);
-void ipath_driver_remove_group(struct device_driver *);
+extern struct attribute_group *ipath_driver_attr_groups[];
int ipath_device_create_group(struct device *, struct ipath_devdata *);
void ipath_device_remove_group(struct device *, struct ipath_devdata *);
diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c
index e1ad7cfc21fd..aa27ca9f03b1 100644
--- a/drivers/infiniband/hw/ipath/ipath_sysfs.c
+++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c
@@ -683,6 +683,11 @@ static struct attribute_group driver_attr_group = {
.attrs = driver_attributes
};
+struct attribute_group *ipath_driver_attr_groups[] = {
+ &driver_attr_group,
+ NULL,
+};
+
static DEVICE_ATTR(guid, S_IWUSR | S_IRUGO, show_guid, store_guid);
static DEVICE_ATTR(lmc, S_IWUSR | S_IRUGO, show_lmc, store_lmc);
static DEVICE_ATTR(lid, S_IWUSR | S_IRUGO, show_lid, store_lid);
@@ -753,24 +758,9 @@ int ipath_expose_reset(struct device *dev)
return ret;
}
-int ipath_driver_create_group(struct device_driver *drv)
-{
- int ret;
-
- ret = sysfs_create_group(&drv->kobj, &driver_attr_group);
-
- return ret;
-}
-
-void ipath_driver_remove_group(struct device_driver *drv)
-{
- sysfs_remove_group(&drv->kobj, &driver_attr_group);
-}
-
int ipath_device_create_group(struct device *dev, struct ipath_devdata *dd)
{
int ret;
- char unit[5];
ret = sysfs_create_group(&dev->kobj, &dev_attr_group);
if (ret)
@@ -780,11 +770,6 @@ int ipath_device_create_group(struct device *dev, struct ipath_devdata *dd)
if (ret)
goto bail_attrs;
- snprintf(unit, sizeof(unit), "%02d", dd->ipath_unit);
- ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, unit);
- if (ret == 0)
- goto bail;
-
sysfs_remove_group(&dev->kobj, &dev_counter_attr_group);
bail_attrs:
sysfs_remove_group(&dev->kobj, &dev_attr_group);
@@ -794,11 +779,6 @@ bail:
void ipath_device_remove_group(struct device *dev, struct ipath_devdata *dd)
{
- char unit[5];
-
- snprintf(unit, sizeof(unit), "%02d", dd->ipath_unit);
- sysfs_remove_link(&dev->driver->kobj, unit);
-
sysfs_remove_group(&dev->kobj, &dev_counter_attr_group);
sysfs_remove_group(&dev->kobj, &dev_attr_group);
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index f449daef3eed..23ae66c76d47 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -1544,11 +1544,11 @@ static int __init capi_init(void)
return PTR_ERR(capi_class);
}
- class_device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi");
+ device_create(capi_class, NULL, MKDEV(capi_major, 0), "capi");
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
if (capinc_tty_init() < 0) {
- class_device_destroy(capi_class, MKDEV(capi_major, 0));
+ device_destroy(capi_class, MKDEV(capi_major, 0));
class_destroy(capi_class);
unregister_chrdev(capi_major, "capi20");
return -ENOMEM;
@@ -1576,7 +1576,7 @@ static void __exit capi_exit(void)
{
proc_exit();
- class_device_destroy(capi_class, MKDEV(capi_major, 0));
+ device_destroy(capi_class, MKDEV(capi_major, 0));
class_destroy(capi_class);
unregister_chrdev(capi_major, "capi20");
diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c
index 48c1775ef5b3..cb42b690b45e 100644
--- a/drivers/isdn/capi/capidrv.c
+++ b/drivers/isdn/capi/capidrv.c
@@ -2332,13 +2332,14 @@ static int __init capidrv_init(void)
static void __exit capidrv_exit(void)
{
- char rev[10];
+ char rev[32];
char *p;
if ((p = strchr(revision, ':')) != 0) {
- strcpy(rev, p + 1);
- p = strchr(rev, '$');
- *p = 0;
+ strncpy(rev, p + 1, sizeof(rev));
+ rev[sizeof(rev)-1] = 0;
+ if ((p = strchr(rev, '$')) != 0)
+ *p = 0;
} else {
strcpy(rev, " ??? ");
}
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index a0317abaeb11..02bdaf22d7ea 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -106,12 +106,6 @@ enum debuglevel {
activated */
};
-/* missing from linux/device.h ... */
-#ifndef dev_notice
-#define dev_notice(dev, format, arg...) \
- dev_printk(KERN_NOTICE , dev , format , ## arg)
-#endif
-
/* Kernel message macros for situations where dev_printk and friends cannot be
* used for lack of reliable access to a device structure.
* linux/usb.h already contains these but in an obsolete form which clutters
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
index 47c10b8f89b3..c0f372f1d761 100644
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -3451,7 +3451,7 @@ static int kvm_resume(struct sys_device *dev)
}
static struct sysdev_class kvm_sysdev_class = {
- set_kset_name("kvm"),
+ .name = "kvm",
.suspend = kvm_suspend,
.resume = kvm_resume,
};
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 5c742a526082..b7adde4324e4 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -875,5 +875,5 @@ adbdev_init(void)
adb_dev_class = class_create(THIS_MODULE, "adb");
if (IS_ERR(adb_dev_class))
return;
- class_device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, "adb");
+ device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), "adb");
}
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 6123c70153d3..ac420b17e16f 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -2796,7 +2796,7 @@ static int pmu_sys_resume(struct sys_device *sysdev)
#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
static struct sysdev_class pmu_sysclass = {
- set_kset_name("pmu"),
+ .name = "pmu",
};
static struct sys_device device_pmu = {
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 88c0fd657825..f2d24eb3208c 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1109,7 +1109,7 @@ static void event_callback(void *context)
list_splice_init(&md->uevent_list, &uevents);
spin_unlock_irqrestore(&md->uevent_lock, flags);
- dm_send_uevents(&uevents, &md->disk->kobj);
+ dm_send_uevents(&uevents, &md->disk->dev.kobj);
atomic_inc(&md->event_nr);
wake_up(&md->eventq);
@@ -1530,7 +1530,7 @@ out:
*---------------------------------------------------------------*/
void dm_kobject_uevent(struct mapped_device *md)
{
- kobject_uevent(&md->disk->kobj, KOBJ_CHANGE);
+ kobject_uevent(&md->disk->dev.kobj, KOBJ_CHANGE);
}
uint32_t dm_next_uevent_seq(struct mapped_device *md)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index cef9ebd5a046..c28a120b4161 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -231,7 +231,7 @@ static void mddev_put(mddev_t *mddev)
list_del(&mddev->all_mddevs);
spin_unlock(&all_mddevs_lock);
blk_cleanup_queue(mddev->queue);
- kobject_unregister(&mddev->kobj);
+ kobject_put(&mddev->kobj);
} else
spin_unlock(&all_mddevs_lock);
}
@@ -1383,22 +1383,19 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
return -EBUSY;
}
bdevname(rdev->bdev,b);
- if (kobject_set_name(&rdev->kobj, "dev-%s", b) < 0)
- return -ENOMEM;
- while ( (s=strchr(rdev->kobj.k_name, '/')) != NULL)
+ while ( (s=strchr(b, '/')) != NULL)
*s = '!';
-
+
rdev->mddev = mddev;
printk(KERN_INFO "md: bind<%s>\n", b);
- rdev->kobj.parent = &mddev->kobj;
- if ((err = kobject_add(&rdev->kobj)))
+ if ((err = kobject_add(&rdev->kobj, &mddev->kobj, "dev-%s", b)))
goto fail;
if (rdev->bdev->bd_part)
- ko = &rdev->bdev->bd_part->kobj;
+ ko = &rdev->bdev->bd_part->dev.kobj;
else
- ko = &rdev->bdev->bd_disk->kobj;
+ ko = &rdev->bdev->bd_disk->dev.kobj;
if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) {
kobject_del(&rdev->kobj);
goto fail;
@@ -2036,9 +2033,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
if (err)
goto abort_free;
- rdev->kobj.parent = NULL;
- rdev->kobj.ktype = &rdev_ktype;
- kobject_init(&rdev->kobj);
+ kobject_init(&rdev->kobj, &rdev_ktype);
rdev->desc_nr = -1;
rdev->saved_raid_disk = -1;
@@ -3054,6 +3049,7 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data)
int partitioned = (MAJOR(dev) != MD_MAJOR);
int shift = partitioned ? MdpMinorShift : 0;
int unit = MINOR(dev) >> shift;
+ int error;
if (!mddev)
return NULL;
@@ -3082,12 +3078,13 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data)
add_disk(disk);
mddev->gendisk = disk;
mutex_unlock(&disks_mutex);
- mddev->kobj.parent = &disk->kobj;
- kobject_set_name(&mddev->kobj, "%s", "md");
- mddev->kobj.ktype = &md_ktype;
- if (kobject_register(&mddev->kobj))
+ error = kobject_init_and_add(&mddev->kobj, &md_ktype, &disk->dev.kobj,
+ "%s", "md");
+ if (error)
printk(KERN_WARNING "md: cannot register %s/md - name in use\n",
disk->disk_name);
+ else
+ kobject_uevent(&mddev->kobj, KOBJ_ADD);
return NULL;
}
@@ -3359,7 +3356,7 @@ static int do_md_run(mddev_t * mddev)
mddev->changed = 1;
md_new_event(mddev);
- kobject_uevent(&mddev->gendisk->kobj, KOBJ_CHANGE);
+ kobject_uevent(&mddev->gendisk->dev.kobj, KOBJ_CHANGE);
return 0;
}
diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c
index e325fa71f38b..b7c8e7813865 100644
--- a/drivers/mfd/ucb1x00-assabet.c
+++ b/drivers/mfd/ucb1x00-assabet.c
@@ -20,7 +20,8 @@
#include "ucb1x00.h"
#define UCB1X00_ATTR(name,input)\
-static ssize_t name##_show(struct class_device *dev, char *buf) \
+static ssize_t name##_show(struct device *dev, struct device_attribute *attr,
+ char *buf) \
{ \
struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \
int val; \
@@ -29,7 +30,7 @@ static ssize_t name##_show(struct class_device *dev, char *buf) \
ucb1x00_adc_disable(ucb); \
return sprintf(buf, "%d\n", val); \
} \
-static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL)
+static DEVICE_ATTR(name,0444,name##_show,NULL)
UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1);
UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD0);
@@ -37,17 +38,17 @@ UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2);
static int ucb1x00_assabet_add(struct ucb1x00_dev *dev)
{
- class_device_create_file(&dev->ucb->cdev, &class_device_attr_vbatt);
- class_device_create_file(&dev->ucb->cdev, &class_device_attr_vcharger);
- class_device_create_file(&dev->ucb->cdev, &class_device_attr_batt_temp);
+ device_create_file(&dev->ucb->dev, &device_attr_vbatt);
+ device_create_file(&dev->ucb->dev, &device_attr_vcharger);
+ device_create_file(&dev->ucb->dev, &device_attr_batt_temp);
return 0;
}
static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev)
{
- class_device_remove_file(&dev->ucb->cdev, &class_device_attr_batt_temp);
- class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vcharger);
- class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vbatt);
+ device_remove_file(&dev->ucb->cdev, &device_attr_batt_temp);
+ device_remove_file(&dev->ucb->cdev, &device_attr_vcharger);
+ device_remove_file(&dev->ucb->cdev, &device_attr_vbatt);
}
static struct ucb1x00_driver ucb1x00_assabet_driver = {
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index e03f1bcd4f9f..f6b10dda31fd 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -458,7 +458,7 @@ static int ucb1x00_detect_irq(struct ucb1x00 *ucb)
return probe_irq_off(mask);
}
-static void ucb1x00_release(struct class_device *dev)
+static void ucb1x00_release(struct device *dev)
{
struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
kfree(ucb);
@@ -466,7 +466,7 @@ static void ucb1x00_release(struct class_device *dev)
static struct class ucb1x00_class = {
.name = "ucb1x00",
- .release = ucb1x00_release,
+ .dev_release = ucb1x00_release,
};
static int ucb1x00_probe(struct mcp *mcp)
@@ -490,9 +490,9 @@ static int ucb1x00_probe(struct mcp *mcp)
goto err_disable;
- ucb->cdev.class = &ucb1x00_class;
- ucb->cdev.dev = &mcp->attached_device;
- strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id));
+ ucb->dev.class = &ucb1x00_class;
+ ucb->dev.parent = &mcp->attached_device;
+ strlcpy(ucb->dev.bus_id, "ucb1x00", sizeof(ucb->dev.bus_id));
spin_lock_init(&ucb->lock);
spin_lock_init(&ucb->io_lock);
@@ -517,7 +517,7 @@ static int ucb1x00_probe(struct mcp *mcp)
mcp_set_drvdata(mcp, ucb);
- ret = class_device_register(&ucb->cdev);
+ ret = device_register(&ucb->dev);
if (ret)
goto err_irq;
@@ -554,7 +554,7 @@ static void ucb1x00_remove(struct mcp *mcp)
mutex_unlock(&ucb1x00_mutex);
free_irq(ucb->irq, ucb);
- class_device_unregister(&ucb->cdev);
+ device_unregister(&ucb->dev);
}
int ucb1x00_register_driver(struct ucb1x00_driver *drv)
diff --git a/drivers/mfd/ucb1x00.h b/drivers/mfd/ucb1x00.h
index ca8df8072d43..a8ad8a0ed5db 100644
--- a/drivers/mfd/ucb1x00.h
+++ b/drivers/mfd/ucb1x00.h
@@ -120,7 +120,7 @@ struct ucb1x00 {
u16 irq_fal_enbl;
u16 irq_ris_enbl;
struct ucb1x00_irq irq_handler[16];
- struct class_device cdev;
+ struct device dev;
struct list_head node;
struct list_head devs;
};
@@ -144,7 +144,7 @@ struct ucb1x00_driver {
int (*resume)(struct ucb1x00_dev *dev);
};
-#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, cdev)
+#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, dev)
int ucb1x00_register_driver(struct ucb1x00_driver *);
void ucb1x00_unregister_driver(struct ucb1x00_driver *);
diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c
index 6497872df524..1a0e7978226a 100644
--- a/drivers/misc/ibmasm/command.c
+++ b/drivers/misc/ibmasm/command.c
@@ -26,11 +26,6 @@
#include "lowlevel.h"
static void exec_next_command(struct service_processor *sp);
-static void free_command(struct kobject *kobj);
-
-static struct kobj_type ibmasm_cmd_kobj_type = {
- .release = free_command,
-};
static atomic_t command_count = ATOMIC_INIT(0);
@@ -53,8 +48,7 @@ struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_s
}
cmd->buffer_size = buffer_size;
- kobject_init(&cmd->kobj);
- cmd->kobj.ktype = &ibmasm_cmd_kobj_type;
+ kref_init(&cmd->kref);
cmd->lock = &sp->lock;
cmd->status = IBMASM_CMD_PENDING;
@@ -67,9 +61,9 @@ struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_s
return cmd;
}
-static void free_command(struct kobject *kobj)
+void ibmasm_free_command(struct kref *kref)
{
- struct command *cmd = to_command(kobj);
+ struct command *cmd = to_command(kref);
list_del(&cmd->queue_node);
atomic_dec(&command_count);
diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h
index de860bc6d3f5..4d8a4e248b34 100644
--- a/drivers/misc/ibmasm/ibmasm.h
+++ b/drivers/misc/ibmasm/ibmasm.h
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/kref.h>
#include <linux/device.h>
#include <linux/input.h>
@@ -92,24 +93,25 @@ struct command {
unsigned char *buffer;
size_t buffer_size;
int status;
- struct kobject kobj;
+ struct kref kref;
spinlock_t *lock;
};
-#define to_command(c) container_of(c, struct command, kobj)
+#define to_command(c) container_of(c, struct command, kref)
+void ibmasm_free_command(struct kref *kref);
static inline void command_put(struct command *cmd)
{
unsigned long flags;
spinlock_t *lock = cmd->lock;
spin_lock_irqsave(lock, flags);
- kobject_put(&cmd->kobj);
+ kref_put(&cmd->kref, ibmasm_free_command);
spin_unlock_irqrestore(lock, flags);
}
static inline void command_get(struct command *cmd)
{
- kobject_get(&cmd->kobj);
+ kref_get(&cmd->kref);
}
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c
index 2d1b3df95c5b..54380da343a5 100644
--- a/drivers/misc/tifm_7xx1.c
+++ b/drivers/misc/tifm_7xx1.c
@@ -149,7 +149,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work)
socket_change_set = fm->socket_change_set;
fm->socket_change_set = 0;
- dev_dbg(fm->cdev.dev, "checking media set %x\n",
+ dev_dbg(fm->dev.parent, "checking media set %x\n",
socket_change_set);
if (!socket_change_set) {
@@ -164,7 +164,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work)
if (sock) {
printk(KERN_INFO
"%s : demand removing card from socket %u:%u\n",
- fm->cdev.class_id, fm->id, cnt);
+ fm->dev.bus_id, fm->id, cnt);
fm->sockets[cnt] = NULL;
sock_addr = sock->addr;
spin_unlock_irqrestore(&fm->lock, flags);
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c
index 8f77949f93dd..97544052e768 100644
--- a/drivers/misc/tifm_core.c
+++ b/drivers/misc/tifm_core.c
@@ -160,16 +160,16 @@ static struct bus_type tifm_bus_type = {
.resume = tifm_device_resume
};
-static void tifm_free(struct class_device *cdev)
+static void tifm_free(struct device *dev)
{
- struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev);
+ struct tifm_adapter *fm = container_of(dev, struct tifm_adapter, dev);
kfree(fm);
}
static struct class tifm_adapter_class = {
.name = "tifm_adapter",
- .release = tifm_free
+ .dev_release = tifm_free
};
struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets,
@@ -180,9 +180,9 @@ struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets,
fm = kzalloc(sizeof(struct tifm_adapter)
+ sizeof(struct tifm_dev*) * num_sockets, GFP_KERNEL);
if (fm) {
- fm->cdev.class = &tifm_adapter_class;
- fm->cdev.dev = dev;
- class_device_initialize(&fm->cdev);
+ fm->dev.class = &tifm_adapter_class;
+ fm->dev.parent = dev;
+ device_initialize(&fm->dev);
spin_lock_init(&fm->lock);
fm->num_sockets = num_sockets;
}
@@ -203,8 +203,8 @@ int tifm_add_adapter(struct tifm_adapter *fm)
if (rc)
return rc;
- snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id);
- rc = class_device_add(&fm->cdev);
+ snprintf(fm->dev.bus_id, BUS_ID_SIZE, "tifm%u", fm->id);
+ rc = device_add(&fm->dev);
if (rc) {
spin_lock(&tifm_adapter_lock);
idr_remove(&tifm_adapter_idr, fm->id);
@@ -228,13 +228,13 @@ void tifm_remove_adapter(struct tifm_adapter *fm)
spin_lock(&tifm_adapter_lock);
idr_remove(&tifm_adapter_idr, fm->id);
spin_unlock(&tifm_adapter_lock);
- class_device_del(&fm->cdev);
+ device_del(&fm->dev);
}
EXPORT_SYMBOL(tifm_remove_adapter);
void tifm_free_adapter(struct tifm_adapter *fm)
{
- class_device_put(&fm->cdev);
+ put_device(&fm->dev);
}
EXPORT_SYMBOL(tifm_free_adapter);
@@ -261,9 +261,9 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
sock->card_event = tifm_dummy_event;
sock->data_event = tifm_dummy_event;
- sock->dev.parent = fm->cdev.dev;
+ sock->dev.parent = fm->dev.parent;
sock->dev.bus = &tifm_bus_type;
- sock->dev.dma_mask = fm->cdev.dev->dma_mask;
+ sock->dev.dma_mask = fm->dev.parent->dma_mask;
sock->dev.release = tifm_free_device;
snprintf(sock->dev.bus_id, BUS_ID_SIZE,
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 22ed96c4b7bd..a0cee86464ca 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -27,12 +27,10 @@ static void mtd_notify_add(struct mtd_info* mtd)
if (!mtd)
return;
- class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
- NULL, "mtd%d", mtd->index);
+ device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), "mtd%d", mtd->index);
- class_device_create(mtd_class, NULL,
- MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
- NULL, "mtd%dro", mtd->index);
+ device_create(mtd_class, NULL,
+ MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), "mtd%dro", mtd->index);
}
static void mtd_notify_remove(struct mtd_info* mtd)
@@ -40,8 +38,8 @@ static void mtd_notify_remove(struct mtd_info* mtd)
if (!mtd)
return;
- class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
- class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
+ device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
+ device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
}
static struct mtd_notifier notifier = {
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index 7d7758f3ad8c..57772bebff56 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -1179,13 +1179,15 @@ static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_
for(i = 0; i<IbmVethNumBufferPools; i++) {
struct kobject *kobj = &adapter->rx_buff_pool[i].kobj;
+ int error;
+
ibmveth_init_buffer_pool(&adapter->rx_buff_pool[i], i,
pool_count[i], pool_size[i],
pool_active[i]);
- kobj->parent = &dev->dev.kobj;
- kobject_set_name(kobj, "pool%d", i);
- kobj->ktype = &ktype_veth_pool;
- kobject_register(kobj);
+ error = kobject_init_and_add(kobj, &ktype_veth_pool,
+ &dev->dev.kobj, "pool%d", i);
+ if (!error)
+ kobject_uevent(kobj, KOBJ_ADD);
}
ibmveth_debug_printk("adapter @ 0x%p\n", adapter);
@@ -1234,7 +1236,7 @@ static int __devexit ibmveth_remove(struct vio_dev *dev)
int i;
for(i = 0; i<IbmVethNumBufferPools; i++)
- kobject_unregister(&adapter->rx_buff_pool[i].kobj);
+ kobject_put(&adapter->rx_buff_pool[i].kobj);
unregister_netdev(netdev);
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index 97bd9dc2e52e..419861cbc65e 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -815,7 +815,7 @@ static int veth_init_connection(u8 rlp)
{
struct veth_lpar_connection *cnx;
struct veth_msg *msgs;
- int i, rc;
+ int i;
if ( (rlp == this_lp)
|| ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) )
@@ -844,11 +844,7 @@ static int veth_init_connection(u8 rlp)
/* This gets us 1 reference, which is held on behalf of the driver
* infrastructure. It's released at module unload. */
- kobject_init(&cnx->kobject);
- cnx->kobject.ktype = &veth_lpar_connection_ktype;
- rc = kobject_set_name(&cnx->kobject, "cnx%.2d", rlp);
- if (rc != 0)
- return rc;
+ kobject_init(&cnx->kobject, &veth_lpar_connection_ktype);
msgs = kcalloc(VETH_NUMBUFFERS, sizeof(struct veth_msg), GFP_KERNEL);
if (! msgs) {
@@ -1087,11 +1083,8 @@ static struct net_device * __init veth_probe_one(int vlan,
return NULL;
}
- kobject_init(&port->kobject);
- port->kobject.parent = &dev->dev.kobj;
- port->kobject.ktype = &veth_port_ktype;
- kobject_set_name(&port->kobject, "veth_port");
- if (0 != kobject_add(&port->kobject))
+ kobject_init(&port->kobject, &veth_port_ktype);
+ if (0 != kobject_add(&port->kobject, &dev->dev.kobj, "veth_port"))
veth_error("Failed adding port for %s to sysfs.\n", dev->name);
veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n",
@@ -1711,9 +1704,9 @@ static int __init veth_module_init(void)
continue;
kobj = &veth_cnx[i]->kobject;
- kobj->parent = &veth_driver.driver.kobj;
/* If the add failes, complain but otherwise continue */
- if (0 != kobject_add(kobj))
+ if (0 != driver_add_kobj(&veth_driver.driver, kobj,
+ "cnx%.2d", veth_cnx[i]->remote_lp))
veth_error("cnx %d: Failed adding to sysfs.\n", i);
}
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index ff37bf437a99..1d706eae3052 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -395,8 +395,7 @@ static int __init cosa_init(void)
goto out_chrdev;
}
for (i=0; i<nr_cards; i++) {
- class_device_create(cosa_class, NULL, MKDEV(cosa_major, i),
- NULL, "cosa%d", i);
+ device_create(cosa_class, NULL, MKDEV(cosa_major, i), "cosa%d", i);
}
err = 0;
goto out;
@@ -415,7 +414,7 @@ static void __exit cosa_exit(void)
printk(KERN_INFO "Unloading the cosa module\n");
for (i=0; i<nr_cards; i++)
- class_device_destroy(cosa_class, MKDEV(cosa_major, i));
+ device_destroy(cosa_class, MKDEV(cosa_major, i));
class_destroy(cosa_class);
for (cosa=cosa_cards; nr_cards--; cosa++) {
/* Clean up the per-channel data */
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
index ebb09e98d215..de34aa9d3136 100644
--- a/drivers/parisc/pdc_stable.c
+++ b/drivers/parisc/pdc_stable.c
@@ -120,7 +120,7 @@ struct pdcspath_entry pdcspath_entry_##_name = { \
};
#define PDCS_ATTR(_name, _mode, _show, _store) \
-struct subsys_attribute pdcs_attr_##_name = { \
+struct kobj_attribute pdcs_attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode}, \
.show = _show, \
.store = _store, \
@@ -523,15 +523,15 @@ static struct pdcspath_entry *pdcspath_entries[] = {
/**
* pdcs_size_read - Stable Storage size output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*/
-static ssize_t
-pdcs_size_read(struct kset *kset, char *buf)
+static ssize_t pdcs_size_read(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
{
char *out = buf;
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
/* show the size of the stable storage */
@@ -542,17 +542,17 @@ pdcs_size_read(struct kset *kset, char *buf)
/**
* pdcs_auto_read - Stable Storage autoboot/search flag output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
* @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
*/
-static ssize_t
-pdcs_auto_read(struct kset *kset, char *buf, int knob)
+static ssize_t pdcs_auto_read(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf, int knob)
{
char *out = buf;
struct pdcspath_entry *pathentry;
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
/* Current flags are stored in primary boot path entry */
@@ -568,40 +568,37 @@ pdcs_auto_read(struct kset *kset, char *buf, int knob)
/**
* pdcs_autoboot_read - Stable Storage autoboot flag output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*/
-static inline ssize_t
-pdcs_autoboot_read(struct kset *kset, char *buf)
+static ssize_t pdcs_autoboot_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
- return pdcs_auto_read(kset, buf, PF_AUTOBOOT);
+ return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT);
}
/**
* pdcs_autosearch_read - Stable Storage autoboot flag output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*/
-static inline ssize_t
-pdcs_autosearch_read(struct kset *kset, char *buf)
+static ssize_t pdcs_autosearch_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
- return pdcs_auto_read(kset, buf, PF_AUTOSEARCH);
+ return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH);
}
/**
* pdcs_timer_read - Stable Storage timer count output (in seconds).
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*
* The value of the timer field correponds to a number of seconds in powers of 2.
*/
-static ssize_t
-pdcs_timer_read(struct kset *kset, char *buf)
+static ssize_t pdcs_timer_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
char *out = buf;
struct pdcspath_entry *pathentry;
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
/* Current flags are stored in primary boot path entry */
@@ -618,15 +615,14 @@ pdcs_timer_read(struct kset *kset, char *buf)
/**
* pdcs_osid_read - Stable Storage OS ID register output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*/
-static ssize_t
-pdcs_osid_read(struct kset *kset, char *buf)
+static ssize_t pdcs_osid_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
char *out = buf;
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
out += sprintf(out, "%s dependent data (0x%.4x)\n",
@@ -637,18 +633,17 @@ pdcs_osid_read(struct kset *kset, char *buf)
/**
* pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*
* This can hold 16 bytes of OS-Dependent data.
*/
-static ssize_t
-pdcs_osdep1_read(struct kset *kset, char *buf)
+static ssize_t pdcs_osdep1_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
char *out = buf;
u32 result[4];
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK)
@@ -664,18 +659,17 @@ pdcs_osdep1_read(struct kset *kset, char *buf)
/**
* pdcs_diagnostic_read - Stable Storage Diagnostic register output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*
* I have NFC how to interpret the content of that register ;-).
*/
-static ssize_t
-pdcs_diagnostic_read(struct kset *kset, char *buf)
+static ssize_t pdcs_diagnostic_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
char *out = buf;
u32 result;
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
/* get diagnostic */
@@ -689,18 +683,17 @@ pdcs_diagnostic_read(struct kset *kset, char *buf)
/**
* pdcs_fastsize_read - Stable Storage FastSize register output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*
* This register holds the amount of system RAM to be tested during boot sequence.
*/
-static ssize_t
-pdcs_fastsize_read(struct kset *kset, char *buf)
+static ssize_t pdcs_fastsize_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
char *out = buf;
u32 result;
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
/* get fast-size */
@@ -718,13 +711,12 @@ pdcs_fastsize_read(struct kset *kset, char *buf)
/**
* pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The output buffer to write to.
*
* This can hold pdcs_size - 224 bytes of OS-Dependent data, when available.
*/
-static ssize_t
-pdcs_osdep2_read(struct kset *kset, char *buf)
+static ssize_t pdcs_osdep2_read(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
{
char *out = buf;
unsigned long size;
@@ -736,7 +728,7 @@ pdcs_osdep2_read(struct kset *kset, char *buf)
size = pdcs_size - 224;
- if (!kset || !buf)
+ if (!buf)
return -EINVAL;
for (i=0; i<size; i+=4) {
@@ -751,7 +743,6 @@ pdcs_osdep2_read(struct kset *kset, char *buf)
/**
* pdcs_auto_write - This function handles autoboot/search flag modifying.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The input buffer to read from.
* @count: The number of bytes to be read.
* @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
@@ -760,8 +751,9 @@ pdcs_osdep2_read(struct kset *kset, char *buf)
* We expect a precise syntax:
* \"n\" (n == 0 or 1) to toggle AutoBoot Off or On
*/
-static ssize_t
-pdcs_auto_write(struct kset *kset, const char *buf, size_t count, int knob)
+static ssize_t pdcs_auto_write(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf,
+ size_t count, int knob)
{
struct pdcspath_entry *pathentry;
unsigned char flags;
@@ -771,7 +763,7 @@ pdcs_auto_write(struct kset *kset, const char *buf, size_t count, int knob)
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (!kset || !buf || !count)
+ if (!buf || !count)
return -EINVAL;
/* We'll use a local copy of buf */
@@ -826,7 +818,6 @@ parse_error:
/**
* pdcs_autoboot_write - This function handles autoboot flag modifying.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The input buffer to read from.
* @count: The number of bytes to be read.
*
@@ -834,15 +825,15 @@ parse_error:
* We expect a precise syntax:
* \"n\" (n == 0 or 1) to toggle AutoSearch Off or On
*/
-static inline ssize_t
-pdcs_autoboot_write(struct kset *kset, const char *buf, size_t count)
+static ssize_t pdcs_autoboot_write(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
{
- return pdcs_auto_write(kset, buf, count, PF_AUTOBOOT);
+ return pdcs_auto_write(kset, attr, buf, count, PF_AUTOBOOT);
}
/**
* pdcs_autosearch_write - This function handles autosearch flag modifying.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The input buffer to read from.
* @count: The number of bytes to be read.
*
@@ -850,15 +841,15 @@ pdcs_autoboot_write(struct kset *kset, const char *buf, size_t count)
* We expect a precise syntax:
* \"n\" (n == 0 or 1) to toggle AutoSearch Off or On
*/
-static inline ssize_t
-pdcs_autosearch_write(struct kset *kset, const char *buf, size_t count)
+static ssize_t pdcs_autosearch_write(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
{
- return pdcs_auto_write(kset, buf, count, PF_AUTOSEARCH);
+ return pdcs_auto_write(kset, attr, buf, count, PF_AUTOSEARCH);
}
/**
* pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The input buffer to read from.
* @count: The number of bytes to be read.
*
@@ -866,15 +857,16 @@ pdcs_autosearch_write(struct kset *kset, const char *buf, size_t count)
* write approach. It's up to userspace to deal with it when constructing
* its input buffer.
*/
-static ssize_t
-pdcs_osdep1_write(struct kset *kset, const char *buf, size_t count)
+static ssize_t pdcs_osdep1_write(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
{
u8 in[16];
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (!kset || !buf || !count)
+ if (!buf || !count)
return -EINVAL;
if (unlikely(pdcs_osid != OS_ID_LINUX))
@@ -895,7 +887,6 @@ pdcs_osdep1_write(struct kset *kset, const char *buf, size_t count)
/**
* pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input.
- * @kset: An allocated and populated struct kset. We don't use it tho.
* @buf: The input buffer to read from.
* @count: The number of bytes to be read.
*
@@ -903,8 +894,9 @@ pdcs_osdep1_write(struct kset *kset, const char *buf, size_t count)
* byte-by-byte write approach. It's up to userspace to deal with it when
* constructing its input buffer.
*/
-static ssize_t
-pdcs_osdep2_write(struct kset *kset, const char *buf, size_t count)
+static ssize_t pdcs_osdep2_write(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
{
unsigned long size;
unsigned short i;
@@ -913,7 +905,7 @@ pdcs_osdep2_write(struct kset *kset, const char *buf, size_t count)
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (!kset || !buf || !count)
+ if (!buf || !count)
return -EINVAL;
if (unlikely(pdcs_size <= 224))
@@ -951,21 +943,25 @@ static PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL);
static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL);
static PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write);
-static struct subsys_attribute *pdcs_subsys_attrs[] = {
- &pdcs_attr_size,
- &pdcs_attr_autoboot,
- &pdcs_attr_autosearch,
- &pdcs_attr_timer,
- &pdcs_attr_osid,
- &pdcs_attr_osdep1,
- &pdcs_attr_diagnostic,
- &pdcs_attr_fastsize,
- &pdcs_attr_osdep2,
+static struct attribute *pdcs_subsys_attrs[] = {
+ &pdcs_attr_size.attr,
+ &pdcs_attr_autoboot.attr,
+ &pdcs_attr_autosearch.attr,
+ &pdcs_attr_timer.attr,
+ &pdcs_attr_osid.attr,
+ &pdcs_attr_osdep1.attr,
+ &pdcs_attr_diagnostic.attr,
+ &pdcs_attr_fastsize.attr,
+ &pdcs_attr_osdep2.attr,
NULL,
};
-static decl_subsys(paths, &ktype_pdcspath, NULL);
-static decl_subsys(stable, NULL, NULL);
+static struct attribute_group pdcs_attr_group = {
+ .attrs = pdcs_subsys_attrs,
+};
+
+static struct kobject *stable_kobj;
+static struct kset *paths_kset;
/**
* pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage.
@@ -995,12 +991,12 @@ pdcs_register_pathentries(void)
if (err < 0)
continue;
- if ((err = kobject_set_name(&entry->kobj, "%s", entry->name)))
- return err;
- kobj_set_kset_s(entry, paths_subsys);
- if ((err = kobject_register(&entry->kobj)))
+ entry->kobj.kset = paths_kset;
+ err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL,
+ "%s", entry->name);
+ if (err)
return err;
-
+
/* kobject is now registered */
write_lock(&entry->rw_lock);
entry->ready = 2;
@@ -1012,6 +1008,7 @@ pdcs_register_pathentries(void)
}
write_unlock(&entry->rw_lock);
+ kobject_uevent(&entry->kobj, KOBJ_ADD);
}
return 0;
@@ -1029,7 +1026,7 @@ pdcs_unregister_pathentries(void)
for (i = 0; (entry = pdcspath_entries[i]); i++) {
read_lock(&entry->rw_lock);
if (entry->ready >= 2)
- kobject_unregister(&entry->kobj);
+ kobject_put(&entry->kobj);
read_unlock(&entry->rw_lock);
}
}
@@ -1041,8 +1038,7 @@ pdcs_unregister_pathentries(void)
static int __init
pdc_stable_init(void)
{
- struct subsys_attribute *attr;
- int i, rc = 0, error = 0;
+ int rc = 0, error = 0;
u32 result;
/* find the size of the stable storage */
@@ -1062,21 +1058,24 @@ pdc_stable_init(void)
/* the actual result is 16 bits away */
pdcs_osid = (u16)(result >> 16);
- /* For now we'll register the stable subsys within this driver */
- if ((rc = firmware_register(&stable_subsys)))
+ /* For now we'll register the directory at /sys/firmware/stable */
+ stable_kobj = kobject_create_and_add("stable", firmware_kobj);
+ if (!stable_kobj) {
+ rc = -ENOMEM;
goto fail_firmreg;
+ }
/* Don't forget the root entries */
- for (i = 0; (attr = pdcs_subsys_attrs[i]) && !error; i++)
- if (attr->show)
- error = subsys_create_file(&stable_subsys, attr);
-
- /* register the paths subsys as a subsystem of stable subsys */
- kobj_set_kset_s(&paths_subsys, stable_subsys);
- if ((rc = subsystem_register(&paths_subsys)))
- goto fail_subsysreg;
+ error = sysfs_create_group(stable_kobj, pdcs_attr_group);
- /* now we create all "files" for the paths subsys */
+ /* register the paths kset as a child of the stable kset */
+ paths_kset = kset_create_and_add("paths", NULL, stable_kobj);
+ if (!paths_kset) {
+ rc = -ENOMEM;
+ goto fail_ksetreg;
+ }
+
+ /* now we create all "files" for the paths kset */
if ((rc = pdcs_register_pathentries()))
goto fail_pdcsreg;
@@ -1084,10 +1083,10 @@ pdc_stable_init(void)
fail_pdcsreg:
pdcs_unregister_pathentries();
- subsystem_unregister(&paths_subsys);
+ kset_unregister(paths_kset);
-fail_subsysreg:
- firmware_unregister(&stable_subsys);
+fail_ksetreg:
+ kobject_put(stable_kobj);
fail_firmreg:
printk(KERN_INFO PDCS_PREFIX " bailing out\n");
@@ -1098,9 +1097,8 @@ static void __exit
pdc_stable_exit(void)
{
pdcs_unregister_pathentries();
- subsystem_unregister(&paths_subsys);
-
- firmware_unregister(&stable_subsys);
+ kset_unregister(paths_kset);
+ kobject_put(stable_kobj);
}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index 47d26b65e99a..750ebd7a4c10 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -429,7 +429,7 @@ static int __init ibm_acpiphp_init(void)
int retval = 0;
acpi_status status;
struct acpi_device *device;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
dbg("%s\n", __FUNCTION__);
@@ -476,7 +476,7 @@ init_return:
static void __exit ibm_acpiphp_exit(void)
{
acpi_status status;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
dbg("%s\n", __FUNCTION__);
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 01c351c176ac..47bb0e1ff3fa 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -61,7 +61,7 @@ static int debug;
static LIST_HEAD(pci_hotplug_slot_list);
-struct kset pci_hotplug_slots_subsys;
+struct kset *pci_hotplug_slots_kset;
static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
struct attribute *attr, char *buf)
@@ -96,8 +96,6 @@ static struct kobj_type hotplug_slot_ktype = {
.release = &hotplug_slot_release,
};
-decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
-
/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
"33 MHz PCI", /* 0x00 */
@@ -632,18 +630,19 @@ int pci_hp_register (struct hotplug_slot *slot)
return -EINVAL;
}
- kobject_set_name(&slot->kobj, "%s", slot->name);
- kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
-
/* this can fail if we have already registered a slot with the same name */
- if (kobject_register(&slot->kobj)) {
- err("Unable to register kobject");
+ slot->kobj.kset = pci_hotplug_slots_kset;
+ result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL,
+ "%s", slot->name);
+ if (result) {
+ err("Unable to register kobject '%s'", slot->name);
return -EINVAL;
}
-
+
list_add (&slot->slot_list, &pci_hotplug_slot_list);
result = fs_add_slot (slot);
+ kobject_uevent(&slot->kobj, KOBJ_ADD);
dbg ("Added slot %s to the list\n", slot->name);
return result;
}
@@ -672,7 +671,7 @@ int pci_hp_deregister (struct hotplug_slot *slot)
fs_remove_slot (slot);
dbg ("Removed slot %s from the list\n", slot->name);
- kobject_unregister(&slot->kobj);
+ kobject_put(&slot->kobj);
return 0;
}
@@ -700,11 +699,15 @@ int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
static int __init pci_hotplug_init (void)
{
int result;
+ struct kset *pci_bus_kset;
- kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
- result = subsystem_register(&pci_hotplug_slots_subsys);
- if (result) {
- err("Register subsys with error %d\n", result);
+ pci_bus_kset = bus_get_kset(&pci_bus_type);
+
+ pci_hotplug_slots_kset = kset_create_and_add("slots", NULL,
+ &pci_bus_kset->kobj);
+ if (!pci_hotplug_slots_kset) {
+ result = -ENOMEM;
+ err("Register subsys error\n");
goto exit;
}
result = cpci_hotplug_init(debug);
@@ -715,9 +718,9 @@ static int __init pci_hotplug_init (void)
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
goto exit;
-
+
err_subsys:
- subsystem_unregister(&pci_hotplug_slots_subsys);
+ kset_unregister(pci_hotplug_slots_kset);
exit:
return result;
}
@@ -725,7 +728,7 @@ exit:
static void __exit pci_hotplug_exit (void)
{
cpci_hotplug_exit();
- subsystem_unregister(&pci_hotplug_slots_subsys);
+ kset_unregister(pci_hotplug_slots_kset);
}
module_init(pci_hotplug_init);
@@ -737,7 +740,7 @@ MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
-EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
+EXPORT_SYMBOL_GPL(pci_hotplug_slots_kset);
EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index a080fedf0332..e32148a8fa12 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -23,44 +23,13 @@
#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)
+static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t nbytes)
{
char drc_name[MAX_DRC_NAME_LEN];
char *end;
+ int rc;
if (nbytes >= MAX_DRC_NAME_LEN)
return 0;
@@ -72,15 +41,25 @@ static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr,
end = &drc_name[nbytes];
*end = '\0';
- dlpar_attr->rc = dlpar_add_slot(drc_name);
+ rc = dlpar_add_slot(drc_name);
+ if (rc)
+ return rc;
return nbytes;
}
-static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr,
- const char *buf, size_t nbytes)
+static ssize_t add_slot_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t remove_slot_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t nbytes)
{
char drc_name[MAX_DRC_NAME_LEN];
+ int rc;
char *end;
if (nbytes >= MAX_DRC_NAME_LEN)
@@ -93,22 +72,24 @@ static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr,
end = &drc_name[nbytes];
*end = '\0';
- dlpar_attr->rc = dlpar_remove_slot(drc_name);
+ rc = dlpar_remove_slot(drc_name);
+ if (rc)
+ return rc;
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 ssize_t remove_slot_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
-static struct dlpar_io_attr remove_slot_attr = {
- .rc = 0,
- .attr = { .name = REMOVE_SLOT_ATTR_NAME, .mode = 0644},
- .store = remove_slot_store,
-};
+static struct kobj_attribute add_slot_attr =
+ __ATTR(ADD_SLOT_ATTR_NAME, 0644, add_slot_show, add_slot_store);
+
+static struct kobj_attribute remove_slot_attr =
+ __ATTR(REMOVE_SLOT_ATTR_NAME, 0644, remove_slot_show, remove_slot_store);
static struct attribute *default_attrs[] = {
&add_slot_attr.attr,
@@ -116,37 +97,29 @@ static struct attribute *default_attrs[] = {
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,
+static struct attribute_group dlpar_attr_group = {
+ .attrs = default_attrs,
};
-struct kset dlpar_io_kset = {
- .kobj = {.ktype = &ktype_dlpar_io,
- .parent = &pci_hotplug_slots_subsys.kobj},
- .ktype = &ktype_dlpar_io,
-};
+static struct kobject *dlpar_kobj;
int dlpar_sysfs_init(void)
{
- kobject_set_name(&dlpar_io_kset.kobj, DLPAR_KOBJ_NAME);
- if (kset_register(&dlpar_io_kset)) {
- printk(KERN_ERR "rpadlpar_io: cannot register kset for %s\n",
- kobject_name(&dlpar_io_kset.kobj));
+ int error;
+
+ dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME,
+ &pci_hotplug_slots_kset->kobj);
+ if (!dlpar_kobj)
return -EINVAL;
- }
- return 0;
+ error = sysfs_create_group(dlpar_kobj, &dlpar_attr_group);
+ if (error)
+ kobject_put(dlpar_kobj);
+ return error;
}
void dlpar_sysfs_exit(void)
{
- kset_unregister(&dlpar_io_kset);
+ sysfs_remove_group(dlpar_kobj, &dlpar_attr_group);
+ kobject_put(dlpar_kobj);
}
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 6d1a21611818..c4fa35d1dd77 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -1,6 +1,11 @@
/*
* drivers/pci/pci-driver.c
*
+ * (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman <greg@kroah.com>
+ * (C) Copyright 2007 Novell Inc.
+ *
+ * Released under the GPL v2 only.
+ *
*/
#include <linux/pci.h>
@@ -96,17 +101,21 @@ pci_create_newid_file(struct pci_driver *drv)
{
int error = 0;
if (drv->probe != NULL)
- error = sysfs_create_file(&drv->driver.kobj,
- &driver_attr_new_id.attr);
+ error = driver_create_file(&drv->driver, &driver_attr_new_id);
return error;
}
+static void pci_remove_newid_file(struct pci_driver *drv)
+{
+ driver_remove_file(&drv->driver, &driver_attr_new_id);
+}
#else /* !CONFIG_HOTPLUG */
static inline void pci_free_dynids(struct pci_driver *drv) {}
static inline int pci_create_newid_file(struct pci_driver *drv)
{
return 0;
}
+static inline void pci_remove_newid_file(struct pci_driver *drv) {}
#endif
/**
@@ -352,50 +361,6 @@ static void pci_device_shutdown(struct device *dev)
drv->shutdown(pci_dev);
}
-#define kobj_to_pci_driver(obj) container_of(obj, struct device_driver, kobj)
-#define attr_to_driver_attribute(obj) container_of(obj, struct driver_attribute, attr)
-
-static ssize_t
-pci_driver_attr_show(struct kobject * kobj, struct attribute *attr, char *buf)
-{
- struct device_driver *driver = kobj_to_pci_driver(kobj);
- struct driver_attribute *dattr = attr_to_driver_attribute(attr);
- ssize_t ret;
-
- if (!get_driver(driver))
- return -ENODEV;
-
- ret = dattr->show ? dattr->show(driver, buf) : -EIO;
-
- put_driver(driver);
- return ret;
-}
-
-static ssize_t
-pci_driver_attr_store(struct kobject * kobj, struct attribute *attr,
- const char *buf, size_t count)
-{
- struct device_driver *driver = kobj_to_pci_driver(kobj);
- struct driver_attribute *dattr = attr_to_driver_attribute(attr);
- ssize_t ret;
-
- if (!get_driver(driver))
- return -ENODEV;
-
- ret = dattr->store ? dattr->store(driver, buf, count) : -EIO;
-
- put_driver(driver);
- return ret;
-}
-
-static struct sysfs_ops pci_driver_sysfs_ops = {
- .show = pci_driver_attr_show,
- .store = pci_driver_attr_store,
-};
-static struct kobj_type pci_driver_kobj_type = {
- .sysfs_ops = &pci_driver_sysfs_ops,
-};
-
/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
@@ -417,7 +382,6 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
- drv->driver.kobj.ktype = &pci_driver_kobj_type;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
@@ -447,6 +411,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
void
pci_unregister_driver(struct pci_driver *drv)
{
+ pci_remove_newid_file(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c5ca3134513a..5fd585293e79 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1210,16 +1210,19 @@ static void __init pci_sort_breadthfirst_klist(void)
struct klist_node *n;
struct device *dev;
struct pci_dev *pdev;
+ struct klist *device_klist;
- spin_lock(&pci_bus_type.klist_devices.k_lock);
- list_for_each_safe(pos, tmp, &pci_bus_type.klist_devices.k_list) {
+ device_klist = bus_get_device_klist(&pci_bus_type);
+
+ spin_lock(&device_klist->k_lock);
+ list_for_each_safe(pos, tmp, &device_klist->k_list) {
n = container_of(pos, struct klist_node, n_node);
dev = container_of(n, struct device, knode_bus);
pdev = to_pci_dev(dev);
pci_insertion_sort_klist(pdev, &sorted_devices);
}
- list_splice(&sorted_devices, &pci_bus_type.klist_devices.k_list);
- spin_unlock(&pci_bus_type.klist_devices.k_lock);
+ list_splice(&sorted_devices, &device_klist->k_list);
+ spin_unlock(&device_klist->k_lock);
}
static void __init pci_insertion_sort_devices(struct pci_dev *a, struct list_head *list)
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 5cf89a91da1e..15c18f5246d6 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -312,8 +312,7 @@ pcmcia_create_newid_file(struct pcmcia_driver *drv)
{
int error = 0;
if (drv->probe != NULL)
- error = sysfs_create_file(&drv->drv.kobj,
- &driver_attr_new_id.attr);
+ error = driver_create_file(&drv->drv, &driver_attr_new_id);
return error;
}
diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c
index bbf3ee10da04..7e29b90a4f63 100644
--- a/drivers/power/apm_power.c
+++ b/drivers/power/apm_power.c
@@ -13,6 +13,7 @@
#include <linux/power_supply.h>
#include <linux/apm-emulation.h>
+static DEFINE_MUTEX(apm_mutex);
#define PSY_PROP(psy, prop, val) psy->get_property(psy, \
POWER_SUPPLY_PROP_##prop, val)
@@ -23,67 +24,86 @@
static struct power_supply *main_battery;
-static void find_main_battery(void)
-{
- struct device *dev;
- struct power_supply *bat = NULL;
- struct power_supply *max_charge_bat = NULL;
- struct power_supply *max_energy_bat = NULL;
+struct find_bat_param {
+ struct power_supply *main;
+ struct power_supply *bat;
+ struct power_supply *max_charge_bat;
+ struct power_supply *max_energy_bat;
union power_supply_propval full;
- int max_charge = 0;
- int max_energy = 0;
+ int max_charge;
+ int max_energy;
+};
- main_battery = NULL;
+static int __find_main_battery(struct device *dev, void *data)
+{
+ struct find_bat_param *bp = (struct find_bat_param *)data;
- list_for_each_entry(dev, &power_supply_class->devices, node) {
- bat = dev_get_drvdata(dev);
+ bp->bat = dev_get_drvdata(dev);
- if (bat->use_for_apm) {
- /* nice, we explicitly asked to report this battery. */
- main_battery = bat;
- return;
- }
+ if (bp->bat->use_for_apm) {
+ /* nice, we explicitly asked to report this battery. */
+ bp->main = bp->bat;
+ return 1;
+ }
- if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full) ||
- !PSY_PROP(bat, CHARGE_FULL, &full)) {
- if (full.intval > max_charge) {
- max_charge_bat = bat;
- max_charge = full.intval;
- }
- } else if (!PSY_PROP(bat, ENERGY_FULL_DESIGN, &full) ||
- !PSY_PROP(bat, ENERGY_FULL, &full)) {
- if (full.intval > max_energy) {
- max_energy_bat = bat;
- max_energy = full.intval;
- }
+ if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
+ !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
+ if (bp->full.intval > bp->max_charge) {
+ bp->max_charge_bat = bp->bat;
+ bp->max_charge = bp->full.intval;
+ }
+ } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
+ !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
+ if (bp->full.intval > bp->max_energy) {
+ bp->max_energy_bat = bp->bat;
+ bp->max_energy = bp->full.intval;
}
}
+ return 0;
+}
+
+static void find_main_battery(void)
+{
+ struct find_bat_param bp;
+ int error;
+
+ memset(&bp, 0, sizeof(struct find_bat_param));
+ main_battery = NULL;
+ bp.main = main_battery;
+
+ error = class_for_each_device(power_supply_class, &bp,
+ __find_main_battery);
+ if (error) {
+ main_battery = bp.main;
+ return;
+ }
- if ((max_energy_bat && max_charge_bat) &&
- (max_energy_bat != max_charge_bat)) {
+ if ((bp.max_energy_bat && bp.max_charge_bat) &&
+ (bp.max_energy_bat != bp.max_charge_bat)) {
/* try guess battery with more capacity */
- if (!PSY_PROP(max_charge_bat, VOLTAGE_MAX_DESIGN, &full)) {
- if (max_energy > max_charge * full.intval)
- main_battery = max_energy_bat;
+ if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
+ &bp.full)) {
+ if (bp.max_energy > bp.max_charge * bp.full.intval)
+ main_battery = bp.max_energy_bat;
else
- main_battery = max_charge_bat;
- } else if (!PSY_PROP(max_energy_bat, VOLTAGE_MAX_DESIGN,
- &full)) {
- if (max_charge > max_energy / full.intval)
- main_battery = max_charge_bat;
+ main_battery = bp.max_charge_bat;
+ } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
+ &bp.full)) {
+ if (bp.max_charge > bp.max_energy / bp.full.intval)
+ main_battery = bp.max_charge_bat;
else
- main_battery = max_energy_bat;
+ main_battery = bp.max_energy_bat;
} else {
/* give up, choice any */
- main_battery = max_energy_bat;
+ main_battery = bp.max_energy_bat;
}
- } else if (max_charge_bat) {
- main_battery = max_charge_bat;
- } else if (max_energy_bat) {
- main_battery = max_energy_bat;
+ } else if (bp.max_charge_bat) {
+ main_battery = bp.max_charge_bat;
+ } else if (bp.max_energy_bat) {
+ main_battery = bp.max_energy_bat;
} else {
/* give up, try the last if any */
- main_battery = bat;
+ main_battery = bp.bat;
}
}
@@ -207,10 +227,10 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info)
union power_supply_propval status;
union power_supply_propval capacity, time_to_full, time_to_empty;
- down(&power_supply_class->sem);
+ mutex_lock(&apm_mutex);
find_main_battery();
if (!main_battery) {
- up(&power_supply_class->sem);
+ mutex_unlock(&apm_mutex);
return;
}
@@ -278,7 +298,7 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info)
}
}
- up(&power_supply_class->sem);
+ mutex_unlock(&apm_mutex);
}
static int __init apm_battery_init(void)
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index a63b75cf75e2..03d6a38464ef 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -20,28 +20,29 @@
struct class *power_supply_class;
+static int __power_supply_changed_work(struct device *dev, void *data)
+{
+ struct power_supply *psy = (struct power_supply *)data;
+ struct power_supply *pst = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < psy->num_supplicants; i++)
+ if (!strcmp(psy->supplied_to[i], pst->name)) {
+ if (pst->external_power_changed)
+ pst->external_power_changed(pst);
+ }
+ return 0;
+}
+
static void power_supply_changed_work(struct work_struct *work)
{
struct power_supply *psy = container_of(work, struct power_supply,
changed_work);
- int i;
dev_dbg(psy->dev, "%s\n", __FUNCTION__);
- for (i = 0; i < psy->num_supplicants; i++) {
- struct device *dev;
-
- down(&power_supply_class->sem);
- list_for_each_entry(dev, &power_supply_class->devices, node) {
- struct power_supply *pst = dev_get_drvdata(dev);
-
- if (!strcmp(psy->supplied_to[i], pst->name)) {
- if (pst->external_power_changed)
- pst->external_power_changed(pst);
- }
- }
- up(&power_supply_class->sem);
- }
+ class_for_each_device(power_supply_class, psy,
+ __power_supply_changed_work);
power_supply_update_leds(psy);
@@ -55,32 +56,35 @@ void power_supply_changed(struct power_supply *psy)
schedule_work(&psy->changed_work);
}
-int power_supply_am_i_supplied(struct power_supply *psy)
+static int __power_supply_am_i_supplied(struct device *dev, void *data)
{
union power_supply_propval ret = {0,};
- struct device *dev;
-
- down(&power_supply_class->sem);
- list_for_each_entry(dev, &power_supply_class->devices, node) {
- struct power_supply *epsy = dev_get_drvdata(dev);
- int i;
-
- for (i = 0; i < epsy->num_supplicants; i++) {
- if (!strcmp(epsy->supplied_to[i], psy->name)) {
- if (epsy->get_property(epsy,
- POWER_SUPPLY_PROP_ONLINE, &ret))
- continue;
- if (ret.intval)
- goto out;
- }
+ struct power_supply *psy = (struct power_supply *)data;
+ struct power_supply *epsy = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < epsy->num_supplicants; i++) {
+ if (!strcmp(epsy->supplied_to[i], psy->name)) {
+ if (epsy->get_property(epsy,
+ POWER_SUPPLY_PROP_ONLINE, &ret))
+ continue;
+ if (ret.intval)
+ return ret.intval;
}
}
-out:
- up(&power_supply_class->sem);
+ return 0;
+}
+
+int power_supply_am_i_supplied(struct power_supply *psy)
+{
+ int error;
+
+ error = class_for_each_device(power_supply_class, psy,
+ __power_supply_am_i_supplied);
- dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval);
+ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, error);
- return ret.intval;
+ return error;
}
int power_supply_register(struct device *parent, struct power_supply *psy)
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index f1e00ff54ce8..7e3ad4f3b343 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -251,20 +251,23 @@ void rtc_update_irq(struct rtc_device *rtc,
}
EXPORT_SYMBOL_GPL(rtc_update_irq);
+static int __rtc_match(struct device *dev, void *data)
+{
+ char *name = (char *)data;
+
+ if (strncmp(dev->bus_id, name, BUS_ID_SIZE) == 0)
+ return 1;
+ return 0;
+}
+
struct rtc_device *rtc_class_open(char *name)
{
struct device *dev;
struct rtc_device *rtc = NULL;
- down(&rtc_class->sem);
- list_for_each_entry(dev, &rtc_class->devices, node) {
- if (strncmp(dev->bus_id, name, BUS_ID_SIZE) == 0) {
- dev = get_device(dev);
- if (dev)
- rtc = to_rtc_device(dev);
- break;
- }
- }
+ dev = class_find_device(rtc_class, name, __rtc_match);
+ if (dev)
+ rtc = to_rtc_device(dev);
if (rtc) {
if (!try_module_get(rtc->owner)) {
@@ -272,7 +275,6 @@ struct rtc_device *rtc_class_open(char *name)
rtc = NULL;
}
}
- up(&rtc_class->sem);
return rtc;
}
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index c7ea9381db9f..d6e93f15440e 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -2089,6 +2089,11 @@ static struct attribute_group netiucv_drv_attr_group = {
.attrs = netiucv_drv_attrs,
};
+static struct attribute_group *netiucv_drv_attr_groups[] = {
+ &netiucv_drv_attr_group,
+ NULL,
+};
+
static void netiucv_banner(void)
{
PRINT_INFO("NETIUCV driver initialized\n");
@@ -2113,7 +2118,6 @@ static void __exit netiucv_exit(void)
netiucv_unregister_device(dev);
}
- sysfs_remove_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
driver_unregister(&netiucv_driver);
iucv_unregister(&netiucv_handler, 1);
iucv_unregister_dbf_views();
@@ -2133,6 +2137,7 @@ static int __init netiucv_init(void)
if (rc)
goto out_dbf;
IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
+ netiucv_driver.groups = netiucv_drv_attr_groups;
rc = driver_register(&netiucv_driver);
if (rc) {
PRINT_ERR("NETIUCV: failed to register driver.\n");
@@ -2140,18 +2145,9 @@ static int __init netiucv_init(void)
goto out_iucv;
}
- rc = sysfs_create_group(&netiucv_driver.kobj, &netiucv_drv_attr_group);
- if (rc) {
- PRINT_ERR("NETIUCV: failed to add driver attributes.\n");
- IUCV_DBF_TEXT_(setup, 2,
- "ret %d - netiucv_drv_attr_group\n", rc);
- goto out_driver;
- }
netiucv_banner();
return rc;
-out_driver:
- driver_unregister(&netiucv_driver);
out_iucv:
iucv_unregister(&netiucv_handler, 1);
out_dbf:
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index e01cbf152a81..86c3f6539a7d 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -52,6 +52,9 @@ static struct ccw_driver zfcp_ccw_driver = {
.set_offline = zfcp_ccw_set_offline,
.notify = zfcp_ccw_notify,
.shutdown = zfcp_ccw_shutdown,
+ .driver = {
+ .groups = zfcp_driver_attr_groups,
+ },
};
MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
@@ -251,16 +254,7 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
int __init
zfcp_ccw_register(void)
{
- int retval;
-
- retval = ccw_driver_register(&zfcp_ccw_driver);
- if (retval)
- goto out;
- retval = zfcp_sysfs_driver_create_files(&zfcp_ccw_driver.driver);
- if (retval)
- ccw_driver_unregister(&zfcp_ccw_driver);
- out:
- return retval;
+ return ccw_driver_register(&zfcp_ccw_driver);
}
/**
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 8534cf09546c..06b1079b7f3d 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -27,8 +27,7 @@
extern struct zfcp_data zfcp_data;
/******************************** SYSFS *************************************/
-extern int zfcp_sysfs_driver_create_files(struct device_driver *);
-extern void zfcp_sysfs_driver_remove_files(struct device_driver *);
+extern struct attribute_group *zfcp_driver_attr_groups[];
extern int zfcp_sysfs_adapter_create_files(struct device *);
extern void zfcp_sysfs_adapter_remove_files(struct device *);
extern int zfcp_sysfs_port_create_files(struct device *, u32);
diff --git a/drivers/s390/scsi/zfcp_sysfs_driver.c b/drivers/s390/scsi/zfcp_sysfs_driver.c
index 005e62f8593b..651edd58906a 100644
--- a/drivers/s390/scsi/zfcp_sysfs_driver.c
+++ b/drivers/s390/scsi/zfcp_sysfs_driver.c
@@ -98,28 +98,9 @@ static struct attribute_group zfcp_driver_attr_group = {
.attrs = zfcp_driver_attrs,
};
-/**
- * zfcp_sysfs_create_driver_files - create sysfs driver files
- * @dev: pointer to belonging device
- *
- * Create all sysfs attributes of the zfcp device driver
- */
-int
-zfcp_sysfs_driver_create_files(struct device_driver *drv)
-{
- return sysfs_create_group(&drv->kobj, &zfcp_driver_attr_group);
-}
-
-/**
- * zfcp_sysfs_remove_driver_files - remove sysfs driver files
- * @dev: pointer to belonging device
- *
- * Remove all sysfs attributes of the zfcp device driver
- */
-void
-zfcp_sysfs_driver_remove_files(struct device_driver *drv)
-{
- sysfs_remove_group(&drv->kobj, &zfcp_driver_attr_group);
-}
+struct attribute_group *zfcp_driver_attr_groups[] = {
+ &zfcp_driver_attr_group,
+ NULL,
+};
#undef ZFCP_LOG_AREA
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 24271a871b8c..6325115e5b3d 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -429,6 +429,15 @@ void scsi_unregister(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(scsi_unregister);
+static int __scsi_host_match(struct class_device *cdev, void *data)
+{
+ struct Scsi_Host *p;
+ unsigned short *hostnum = (unsigned short *)data;
+
+ p = class_to_shost(cdev);
+ return p->host_no == *hostnum;
+}
+
/**
* scsi_host_lookup - get a reference to a Scsi_Host by host no
*
@@ -439,19 +448,12 @@ EXPORT_SYMBOL(scsi_unregister);
**/
struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
{
- struct class *class = &shost_class;
struct class_device *cdev;
- struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p;
+ struct Scsi_Host *shost = ERR_PTR(-ENXIO);
- down(&class->sem);
- list_for_each_entry(cdev, &class->children, node) {
- p = class_to_shost(cdev);
- if (p->host_no == hostnum) {
- shost = scsi_host_get(p);
- break;
- }
- }
- up(&class->sem);
+ cdev = class_find_child(&shost_class, &hostnum, __scsi_host_match);
+ if (cdev)
+ shost = scsi_host_get(class_to_shost(cdev));
return shost;
}
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index 7663841eb4cf..a3fdc57e2673 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -464,7 +464,7 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd)
res = sas_phy_reset(phy, 1);
if (res)
SAS_DPRINTK("Bus reset of %s failed 0x%x\n",
- phy->dev.kobj.k_name,
+ kobject_name(&phy->dev.kobj),
res);
if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE)
return SUCCESS;
diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c
index 9d3105b64a7a..9c2df5c857cf 100644
--- a/drivers/serial/icom.c
+++ b/drivers/serial/icom.c
@@ -48,7 +48,7 @@
#include <linux/vmalloc.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
-#include <linux/kobject.h>
+#include <linux/kref.h>
#include <linux/firmware.h>
#include <linux/bitops.h>
@@ -65,7 +65,7 @@
#define ICOM_VERSION_STR "1.3.1"
#define NR_PORTS 128
#define ICOM_PORT ((struct icom_port *)port)
-#define to_icom_adapter(d) container_of(d, struct icom_adapter, kobj)
+#define to_icom_adapter(d) container_of(d, struct icom_adapter, kref)
static const struct pci_device_id icom_pci_table[] = {
{
@@ -141,6 +141,7 @@ static inline void trace(struct icom_port *, char *, unsigned long) {};
#else
static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {};
#endif
+static void icom_kref_release(struct kref *kref);
static void free_port_memory(struct icom_port *icom_port)
{
@@ -1063,11 +1064,11 @@ static int icom_open(struct uart_port *port)
{
int retval;
- kobject_get(&ICOM_PORT->adapter->kobj);
+ kref_get(&ICOM_PORT->adapter->kref);
retval = startup(ICOM_PORT);
if (retval) {
- kobject_put(&ICOM_PORT->adapter->kobj);
+ kref_put(&ICOM_PORT->adapter->kref, icom_kref_release);
trace(ICOM_PORT, "STARTUP_ERROR", 0);
return retval;
}
@@ -1088,7 +1089,7 @@ static void icom_close(struct uart_port *port)
shutdown(ICOM_PORT);
- kobject_put(&ICOM_PORT->adapter->kobj);
+ kref_put(&ICOM_PORT->adapter->kref, icom_kref_release);
}
static void icom_set_termios(struct uart_port *port,
@@ -1485,18 +1486,14 @@ static void icom_remove_adapter(struct icom_adapter *icom_adapter)
pci_release_regions(icom_adapter->pci_dev);
}
-static void icom_kobj_release(struct kobject *kobj)
+static void icom_kref_release(struct kref *kref)
{
struct icom_adapter *icom_adapter;
- icom_adapter = to_icom_adapter(kobj);
+ icom_adapter = to_icom_adapter(kref);
icom_remove_adapter(icom_adapter);
}
-static struct kobj_type icom_kobj_type = {
- .release = icom_kobj_release,
-};
-
static int __devinit icom_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
@@ -1592,8 +1589,7 @@ static int __devinit icom_probe(struct pci_dev *dev,
}
}
- kobject_init(&icom_adapter->kobj);
- icom_adapter->kobj.ktype = &icom_kobj_type;
+ kref_init(&icom_adapter->kref);
return 0;
probe_exit2:
@@ -1619,7 +1615,7 @@ static void __devexit icom_remove(struct pci_dev *dev)
icom_adapter = list_entry(tmp, struct icom_adapter,
icom_adapter_entry);
if (icom_adapter->pci_dev == dev) {
- kobject_put(&icom_adapter->kobj);
+ kref_put(&icom_adapter->kref, icom_kref_release);
return;
}
}
diff --git a/drivers/serial/icom.h b/drivers/serial/icom.h
index e8578d8cd35e..027455496745 100644
--- a/drivers/serial/icom.h
+++ b/drivers/serial/icom.h
@@ -270,7 +270,7 @@ struct icom_adapter {
#define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM 0x0251
int numb_ports;
struct list_head icom_adapter_entry;
- struct kobject kobj;
+ struct kref kref;
};
/* prototype */
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 93e9de46977a..682a6a48fec3 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -485,6 +485,15 @@ void spi_unregister_master(struct spi_master *master)
}
EXPORT_SYMBOL_GPL(spi_unregister_master);
+static int __spi_master_match(struct device *dev, void *data)
+{
+ struct spi_master *m;
+ u16 *bus_num = data;
+
+ m = container_of(dev, struct spi_master, dev);
+ return m->bus_num == *bus_num;
+}
+
/**
* spi_busnum_to_master - look up master associated with bus_num
* @bus_num: the master's bus number
@@ -499,17 +508,12 @@ struct spi_master *spi_busnum_to_master(u16 bus_num)
{
struct device *dev;
struct spi_master *master = NULL;
- struct spi_master *m;
-
- down(&spi_master_class.sem);
- list_for_each_entry(dev, &spi_master_class.children, node) {
- m = container_of(dev, struct spi_master, dev);
- if (m->bus_num == bus_num) {
- master = spi_master_get(m);
- break;
- }
- }
- up(&spi_master_class.sem);
+
+ dev = class_find_device(&spi_master_class, &bus_num,
+ __spi_master_match);
+ if (dev)
+ master = container_of(dev, struct spi_master, dev);
+ /* reference got in class_find_device */
return master;
}
EXPORT_SYMBOL_GPL(spi_busnum_to_master);
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 865f32b63b5c..cc246faa3590 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -34,12 +34,12 @@ struct uio_device {
wait_queue_head_t wait;
int vma_count;
struct uio_info *info;
- struct kset map_attr_kset;
+ struct kobject *map_dir;
};
static int uio_major;
static DEFINE_IDR(uio_idr);
-static struct file_operations uio_fops;
+static const struct file_operations uio_fops;
/* UIO class infrastructure */
static struct uio_class {
@@ -51,47 +51,48 @@ static struct uio_class {
* attributes
*/
-static struct attribute attr_addr = {
- .name = "addr",
- .mode = S_IRUGO,
+struct uio_map {
+ struct kobject kobj;
+ struct uio_mem *mem;
};
+#define to_map(map) container_of(map, struct uio_map, kobj)
-static struct attribute attr_size = {
- .name = "size",
- .mode = S_IRUGO,
-};
-static struct attribute* map_attrs[] = {
- &attr_addr, &attr_size, NULL
-};
-
-static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr,
+static ssize_t map_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
- struct uio_mem *mem = container_of(kobj, struct uio_mem, kobj);
+ struct uio_map *map = to_map(kobj);
+ struct uio_mem *mem = map->mem;
- if (strncmp(attr->name,"addr",4) == 0)
+ if (strncmp(attr->attr.name, "addr", 4) == 0)
return sprintf(buf, "0x%lx\n", mem->addr);
- if (strncmp(attr->name,"size",4) == 0)
+ if (strncmp(attr->attr.name, "size", 4) == 0)
return sprintf(buf, "0x%lx\n", mem->size);
return -ENODEV;
}
-static void map_attr_release(struct kobject *kobj)
-{
- /* TODO ??? */
-}
+static struct kobj_attribute attr_attribute =
+ __ATTR(addr, S_IRUGO, map_attr_show, NULL);
+static struct kobj_attribute size_attribute =
+ __ATTR(size, S_IRUGO, map_attr_show, NULL);
-static struct sysfs_ops map_attr_ops = {
- .show = map_attr_show,
+static struct attribute *attrs[] = {
+ &attr_attribute.attr,
+ &size_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
};
+static void map_release(struct kobject *kobj)
+{
+ struct uio_map *map = to_map(kobj);
+ kfree(map);
+}
+
static struct kobj_type map_attr_type = {
- .release = map_attr_release,
- .sysfs_ops = &map_attr_ops,
- .default_attrs = map_attrs,
+ .release = map_release,
+ .default_attrs = attrs,
};
static ssize_t show_name(struct device *dev,
@@ -148,6 +149,7 @@ static int uio_dev_add_attributes(struct uio_device *idev)
int mi;
int map_found = 0;
struct uio_mem *mem;
+ struct uio_map *map;
ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
if (ret)
@@ -159,31 +161,34 @@ static int uio_dev_add_attributes(struct uio_device *idev)
break;
if (!map_found) {
map_found = 1;
- kobject_set_name(&idev->map_attr_kset.kobj,"maps");
- idev->map_attr_kset.ktype = &map_attr_type;
- idev->map_attr_kset.kobj.parent = &idev->dev->kobj;
- ret = kset_register(&idev->map_attr_kset);
- if (ret)
- goto err_remove_group;
+ idev->map_dir = kobject_create_and_add("maps",
+ &idev->dev->kobj);
+ if (!idev->map_dir)
+ goto err;
}
- kobject_init(&mem->kobj);
- kobject_set_name(&mem->kobj,"map%d",mi);
- mem->kobj.parent = &idev->map_attr_kset.kobj;
- mem->kobj.kset = &idev->map_attr_kset;
- ret = kobject_add(&mem->kobj);
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map)
+ goto err;
+ kobject_init(&map->kobj, &map_attr_type);
+ map->mem = mem;
+ mem->map = map;
+ ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
+ if (ret)
+ goto err;
+ ret = kobject_uevent(&map->kobj, KOBJ_ADD);
if (ret)
- goto err_remove_maps;
+ goto err;
}
return 0;
-err_remove_maps:
+err:
for (mi--; mi>=0; mi--) {
mem = &idev->info->mem[mi];
- kobject_unregister(&mem->kobj);
+ map = mem->map;
+ kobject_put(&map->kobj);
}
- kset_unregister(&idev->map_attr_kset); /* Needed ? */
-err_remove_group:
+ kobject_put(idev->map_dir);
sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
err_group:
dev_err(idev->dev, "error creating sysfs files (%d)\n", ret);
@@ -198,9 +203,9 @@ static void uio_dev_del_attributes(struct uio_device *idev)
mem = &idev->info->mem[mi];
if (mem->size == 0)
break;
- kobject_unregister(&mem->kobj);
+ kobject_put(&mem->map->kobj);
}
- kset_unregister(&idev->map_attr_kset);
+ kobject_put(idev->map_dir);
sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
}
@@ -503,7 +508,7 @@ static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
}
}
-static struct file_operations uio_fops = {
+static const struct file_operations uio_fops = {
.owner = THIS_MODULE,
.open = uio_open,
.release = uio_release,
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index c51f8e9312e0..7c3aaa9c5402 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -91,8 +91,8 @@ static int usb_create_newid_file(struct usb_driver *usb_drv)
goto exit;
if (usb_drv->probe != NULL)
- error = sysfs_create_file(&usb_drv->drvwrap.driver.kobj,
- &driver_attr_new_id.attr);
+ error = driver_create_file(&usb_drv->drvwrap.driver,
+ &driver_attr_new_id);
exit:
return error;
}
@@ -103,8 +103,8 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
return;
if (usb_drv->probe != NULL)
- sysfs_remove_file(&usb_drv->drvwrap.driver.kobj,
- &driver_attr_new_id.attr);
+ driver_remove_file(&usb_drv->drvwrap.driver,
+ &driver_attr_new_id);
}
static void usb_free_dynids(struct usb_driver *usb_drv)