core.c 4.17 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7
/*
 * drivers/base/core.c - core driver model code (device registration, etc)
 * 
 * Copyright (c) 2002 Patrick Mochel
 *		 2002 Open Source Development Lab
 */

8 9
#define DEBUG 0

Linus Torvalds's avatar
Linus Torvalds committed
10 11
#include <linux/device.h>
#include <linux/module.h>
12
#include <linux/slab.h>
Dave Jones's avatar
Dave Jones committed
13
#include <linux/err.h>
14
#include <linux/init.h>
Patrick Mochel's avatar
Patrick Mochel committed
15
#include "base.h"
Linus Torvalds's avatar
Linus Torvalds committed
16

17
LIST_HEAD(global_device_list);
Linus Torvalds's avatar
Linus Torvalds committed
18 19 20 21

int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL;

22 23
DECLARE_MUTEX(device_sem);

Patrick Mochel's avatar
Patrick Mochel committed
24
spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
Linus Torvalds's avatar
Linus Torvalds committed
25

Patrick Mochel's avatar
Patrick Mochel committed
26 27
#define to_dev(node) container_of(node,struct device,driver_list)

28
int device_add(struct device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
29 30 31 32 33 34
{
	int error;

	if (!dev || !strlen(dev->bus_id))
		return -EINVAL;

35
	down(&device_sem);
36
	dev->state = DEVICE_REGISTERED;
37
	if (dev->parent) {
38
		list_add_tail(&dev->g_list,&dev->parent->g_list);
39
		list_add_tail(&dev->node,&dev->parent->children);
40 41
	} else
		list_add_tail(&dev->g_list,&global_device_list);
42
	up(&device_sem);
Linus Torvalds's avatar
Linus Torvalds committed
43

44 45
	pr_debug("DEV: registering device: ID = '%s', name = %s\n",
		 dev->bus_id, dev->name);
Linus Torvalds's avatar
Linus Torvalds committed
46 47 48 49

	if ((error = device_make_dir(dev)))
		goto register_done;

50 51
	bus_add_device(dev);

Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55
	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);

56 57 58
	/* notify userspace of device entry */
	dev_hotplug(dev, "add");

59
	devclass_add_device(dev);
Linus Torvalds's avatar
Linus Torvalds committed
60
 register_done:
61
	if (error) {
62
		up(&device_sem);
63 64
		list_del_init(&dev->g_list);
		list_del_init(&dev->node);
65
		up(&device_sem);
66
	}
67 68 69 70 71 72 73 74 75 76 77 78 79
	return error;
}

void device_initialize(struct device *dev)
{
	INIT_LIST_HEAD(&dev->node);
	INIT_LIST_HEAD(&dev->children);
	INIT_LIST_HEAD(&dev->g_list);
	INIT_LIST_HEAD(&dev->driver_list);
	INIT_LIST_HEAD(&dev->bus_list);
	INIT_LIST_HEAD(&dev->intf_list);
	spin_lock_init(&dev->lock);
	atomic_set(&dev->refcount,1);
80
	dev->state = DEVICE_INITIALIZED;
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	if (dev->parent)
		get_device(dev->parent);
}

/**
 * device_register - register a device
 * @dev:	pointer to the device structure
 *
 * First, make sure that the device has a parent, create
 * a directory for it, then add it to the parent's list of
 * children.
 *
 * Maintains a global list of all devices, in depth-first ordering.
 * The head for that list is device_root.g_list.
 */
int device_register(struct device *dev)
{
	int error;

	if (!dev || !strlen(dev->bus_id))
		return -EINVAL;

	device_initialize(dev);
	if (dev->parent)
		get_device(dev->parent);
	error = device_add(dev);
	if (error && dev->parent)
		put_device(dev->parent);
Linus Torvalds's avatar
Linus Torvalds committed
109 110 111
	return error;
}

112
struct device * get_device(struct device * dev)
113 114
{
	struct device * ret = dev;
115
	down(&device_sem);
116
	if (device_present(dev) && atomic_read(&dev->refcount) > 0)
117 118 119
		atomic_inc(&dev->refcount);
	else
		ret = NULL;
120
	up(&device_sem);
121 122 123
	return ret;
}

Linus Torvalds's avatar
Linus Torvalds committed
124
/**
Patrick Mochel's avatar
Patrick Mochel committed
125
 * put_device - decrement reference count, and clean up when it hits 0
Linus Torvalds's avatar
Linus Torvalds committed
126 127 128 129
 * @dev:	device in question
 */
void put_device(struct device * dev)
{
130 131 132
	down(&device_sem);
	if (!atomic_dec_and_test(&dev->refcount)) {
		up(&device_sem);
Linus Torvalds's avatar
Linus Torvalds committed
133
		return;
134
	}
135 136
	list_del_init(&dev->node);
	list_del_init(&dev->g_list);
137
	up(&device_sem);
138

139
	WARN_ON(dev->state != DEVICE_GONE);
140

141
	device_del(dev);
142 143
}

144
void device_del(struct device * dev)
145
{
146
	struct device * parent = dev->parent;
Linus Torvalds's avatar
Linus Torvalds committed
147 148 149 150 151 152 153

	/* Notify the platform of the removal, in case they
	 * need to do anything...
	 */
	if (platform_notify_remove)
		platform_notify_remove(dev);

154 155 156
	/* notify userspace that this device is about to disappear */
	dev_hotplug (dev, "remove");

157 158
	bus_remove_device(dev);

159 160
	/* remove the driverfs directory */
	device_remove_dir(dev);
161 162 163 164 165 166

	if (dev->release)
		dev->release(dev);

	if (parent)
		put_device(parent);
167
}
168

169 170 171 172 173 174 175 176 177 178 179
/**
 * device_unregister - unlink device
 * @dev:	device going away
 *
 * The device has been removed from the system, so we disavow knowledge
 * of it. It might not be the final reference to the device, so we mark
 * it as !present, so no more references to it can be acquired.
 * In the end, we decrement the final reference count for it.
 */
void device_unregister(struct device * dev)
{
180
	down(&device_sem);
181
	dev->state = DEVICE_GONE;
182
	up(&device_sem);
183 184 185

	pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
		 dev->bus_id,dev->name);
186
	put_device(dev);
Andy Grover's avatar
Andy Grover committed
187
}
Linus Torvalds's avatar
Linus Torvalds committed
188 189

EXPORT_SYMBOL(device_register);
190
EXPORT_SYMBOL(device_unregister);
191
EXPORT_SYMBOL(get_device);
Linus Torvalds's avatar
Linus Torvalds committed
192
EXPORT_SYMBOL(put_device);