Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 23681e47 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

[PATCH] Driver core: allow struct device to have a dev_t



This is the first step in moving class_device to being replaced by
struct device.  It allows struct device to export a dev_t and makes it
easy to dynamically create and destroy struct device as long as they are
associated with a specific class.

Cc: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent aa49b913
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -142,6 +142,7 @@ int class_register(struct class * cls)
	pr_debug("device class '%s': registering\n", cls->name);

	INIT_LIST_HEAD(&cls->children);
	INIT_LIST_HEAD(&cls->devices);
	INIT_LIST_HEAD(&cls->interfaces);
	init_MUTEX(&cls->sem);
	error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
+161 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kdev_t.h>

#include <asm/semaphore.h>

@@ -98,6 +99,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
		struct device *dev = to_dev(kobj);
		if (dev->bus)
			return 1;
		if (dev->class)
			return 1;
	}
	return 0;
}
@@ -106,7 +109,11 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
{
	struct device *dev = to_dev(kobj);

	if (dev->bus)
		return dev->bus->name;
	if (dev->class)
		return dev->class->name;
	return NULL;
}

static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
@@ -117,6 +124,16 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
	int length = 0;
	int retval = 0;

	/* add the major/minor if present */
	if (MAJOR(dev->devt)) {
		add_uevent_var(envp, num_envp, &i,
			       buffer, buffer_size, &length,
			       "MAJOR=%u", MAJOR(dev->devt));
		add_uevent_var(envp, num_envp, &i,
			       buffer, buffer_size, &length,
			       "MINOR=%u", MINOR(dev->devt));
	}

	/* add bus name of physical device */
	if (dev->bus)
		add_uevent_var(envp, num_envp, &i,
@@ -161,6 +178,12 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
	return count;
}

static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
			char *buf)
{
	return print_dev_t(buf, dev->devt);
}

/*
 *	devices_subsys - structure to be registered with kobject core.
 */
@@ -231,6 +254,7 @@ void device_initialize(struct device *dev)
	klist_init(&dev->klist_children, klist_children_get,
		   klist_children_put);
	INIT_LIST_HEAD(&dev->dma_pools);
	INIT_LIST_HEAD(&dev->node);
	init_MUTEX(&dev->sem);
	device_init_wakeup(dev, 0);
}
@@ -274,6 +298,31 @@ int device_add(struct device *dev)
	dev->uevent_attr.store = store_uevent;
	device_create_file(dev, &dev->uevent_attr);

	if (MAJOR(dev->devt)) {
		struct device_attribute *attr;
		attr = kzalloc(sizeof(*attr), GFP_KERNEL);
		if (!attr) {
			error = -ENOMEM;
			goto PMError;
		}
		attr->attr.name = "dev";
		attr->attr.mode = S_IRUGO;
		if (dev->driver)
			attr->attr.owner = dev->driver->owner;
		attr->show = show_dev;
		error = device_create_file(dev, attr);
		if (error) {
			kfree(attr);
			goto attrError;
		}

		dev->devt_attr = attr;
	}

	if (dev->class)
		sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
				  dev->bus_id);

	if ((error = device_pm_add(dev)))
		goto PMError;
	if ((error = bus_add_device(dev)))
@@ -292,6 +341,11 @@ int device_add(struct device *dev)
 BusError:
	device_pm_remove(dev);
 PMError:
	if (dev->devt_attr) {
		device_remove_file(dev, dev->devt_attr);
		kfree(dev->devt_attr);
	}
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
@@ -366,6 +420,10 @@ void device_del(struct device * dev)

	if (parent)
		klist_del(&dev->knode_parent);
	if (dev->devt_attr)
		device_remove_file(dev, dev->devt_attr);
	if (dev->class)
		sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
	device_remove_file(dev, &dev->uevent_attr);

	/* Notify the platform of the removal, in case they
@@ -450,3 +508,105 @@ EXPORT_SYMBOL_GPL(put_device);

EXPORT_SYMBOL_GPL(device_create_file);
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);
	kfree(dev);
}

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

	if (class == NULL || IS_ERR(class))
		goto error;
	if (parent == NULL) {
		printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__);
		goto error;
	}

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

	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;
	dev->release = device_create_release;

	va_start(args, fmt);
	vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);
	va_end(args);
	retval = device_register(dev);
	if (retval)
		goto error;

	/* tie the class to the device */
	down(&class->sem);
	list_add_tail(&dev->node, &class->devices);
	up(&class->sem);

	return dev;

error:
	kfree(dev);
	return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(device_create);

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

	down(&class->sem);
	list_for_each_entry(dev_tmp, &class->devices, node) {
		if (dev_tmp->devt == devt) {
			dev = dev_tmp;
			break;
		}
	}
	up(&class->sem);

	if (dev) {
		list_del_init(&dev->node);
		device_unregister(dev);
	}
}
EXPORT_SYMBOL_GPL(device_destroy);
+14 −0
Original line number Diff line number Diff line
@@ -142,6 +142,7 @@ struct class {

	struct subsystem	subsys;
	struct list_head	children;
	struct list_head	devices;
	struct list_head	interfaces;
	struct semaphore	sem;	/* locks both the children and interfaces lists */

@@ -305,6 +306,7 @@ struct device {
	struct kobject kobj;
	char	bus_id[BUS_ID_SIZE];	/* position on parent bus */
	struct device_attribute uevent_attr;
	struct device_attribute *devt_attr;

	struct semaphore	sem;	/* semaphore to synchronize calls to
					 * its driver.
@@ -332,6 +334,11 @@ struct device {
	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */

	/* class_device migration path */
	struct list_head	node;
	struct class		*class;		/* optional*/
	dev_t			devt;		/* dev_t, creates the sysfs "dev" */

	void	(*release)(struct device * dev);
};

@@ -373,6 +380,13 @@ extern int device_attach(struct device * dev);
extern void driver_attach(struct device_driver * drv);
extern void device_reprobe(struct device *dev);

/*
 * Easy functions for dynamically creating devices on the fly
 */
extern struct device *device_create(struct class *cls, struct device *parent,
				    dev_t devt, char *fmt, ...)
				    __attribute__((format(printf,4,5)));
extern void device_destroy(struct class *cls, dev_t devt);

/*
 * Platform "fixup" functions - allow the platform to have their say