Blame view

drivers/base/sys.c 9.65 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * sys.c - pseudo-bus for system 'devices' (cpus, PICs, timers, etc)
   *
   * Copyright (c) 2002-3 Patrick Mochel
   *               2002-3 Open Source Development Lab
   *
   * This file is released under the GPLv2
   *
   * This exports a 'system' bus type.
   * By default, a 'sys' bus gets added to the root of the system. There will
   * always be core system devices. Devices can use sysdev_register() to
   * add themselves as children of the system bus.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
  #include <linux/sysdev.h>
  #include <linux/err.h>
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/string.h>
438510f6f   Pavel Machek   [PATCH] pm_messag...
20
  #include <linux/pm.h>
f67d115fe   Adrian Bunk   [PATCH] drivers/b...
21
  #include <linux/device.h>
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
22
  #include <linux/mutex.h>
2ed8d2b3a   Rafael J. Wysocki   PM: Rework handli...
23
  #include <linux/interrupt.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24

f67d115fe   Adrian Bunk   [PATCH] drivers/b...
25
  #include "base.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
28
29
30
  #define to_sysdev(k) container_of(k, struct sys_device, kobj)
  #define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
  
  
  static ssize_t
60530afe1   Zhenwen Xu   Driver core: some...
31
  sysdev_show(struct kobject *kobj, struct attribute *attr, char *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  {
60530afe1   Zhenwen Xu   Driver core: some...
33
34
  	struct sys_device *sysdev = to_sysdev(kobj);
  	struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
  
  	if (sysdev_attr->show)
4a0b2b4db   Andi Kleen   sysdev: Pass the ...
37
  		return sysdev_attr->show(sysdev, sysdev_attr, buffer);
4a0c20bf8   Dmitry Torokhov   [PATCH] sysfs: (d...
38
  	return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
  }
  
  
  static ssize_t
60530afe1   Zhenwen Xu   Driver core: some...
43
44
  sysdev_store(struct kobject *kobj, struct attribute *attr,
  	     const char *buffer, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
  {
60530afe1   Zhenwen Xu   Driver core: some...
46
47
  	struct sys_device *sysdev = to_sysdev(kobj);
  	struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  
  	if (sysdev_attr->store)
4a0b2b4db   Andi Kleen   sysdev: Pass the ...
50
  		return sysdev_attr->store(sysdev, sysdev_attr, buffer, count);
4a0c20bf8   Dmitry Torokhov   [PATCH] sysfs: (d...
51
  	return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
53
  static const struct sysfs_ops sysfs_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
57
58
59
60
  	.show	= sysdev_show,
  	.store	= sysdev_store,
  };
  
  static struct kobj_type ktype_sysdev = {
  	.sysfs_ops	= &sysfs_ops,
  };
60530afe1   Zhenwen Xu   Driver core: some...
61
  int sysdev_create_file(struct sys_device *s, struct sysdev_attribute *a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  {
  	return sysfs_create_file(&s->kobj, &a->attr);
  }
60530afe1   Zhenwen Xu   Driver core: some...
65
  void sysdev_remove_file(struct sys_device *s, struct sysdev_attribute *a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
69
70
71
  {
  	sysfs_remove_file(&s->kobj, &a->attr);
  }
  
  EXPORT_SYMBOL_GPL(sysdev_create_file);
  EXPORT_SYMBOL_GPL(sysdev_remove_file);
670dd90d8   Shaohua Li   [PATCH] Driver Co...
72
73
74
75
76
77
78
  #define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
  #define to_sysdev_class_attr(a) container_of(a, \
  	struct sysdev_class_attribute, attr)
  
  static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr,
  				 char *buffer)
  {
60530afe1   Zhenwen Xu   Driver core: some...
79
  	struct sysdev_class *class = to_sysdev_class(kobj);
670dd90d8   Shaohua Li   [PATCH] Driver Co...
80
81
82
  	struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
  
  	if (class_attr->show)
c9be0a36f   Andi Kleen   sysdev: Pass attr...
83
  		return class_attr->show(class, class_attr, buffer);
670dd90d8   Shaohua Li   [PATCH] Driver Co...
84
85
86
87
88
89
  	return -EIO;
  }
  
  static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr,
  				  const char *buffer, size_t count)
  {
60530afe1   Zhenwen Xu   Driver core: some...
90
91
  	struct sysdev_class *class = to_sysdev_class(kobj);
  	struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
670dd90d8   Shaohua Li   [PATCH] Driver Co...
92
93
  
  	if (class_attr->store)
c9be0a36f   Andi Kleen   sysdev: Pass attr...
94
  		return class_attr->store(class, class_attr, buffer, count);
670dd90d8   Shaohua Li   [PATCH] Driver Co...
95
96
  	return -EIO;
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
97
  static const struct sysfs_ops sysfs_class_ops = {
670dd90d8   Shaohua Li   [PATCH] Driver Co...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  	.show	= sysdev_class_show,
  	.store	= sysdev_class_store,
  };
  
  static struct kobj_type ktype_sysdev_class = {
  	.sysfs_ops	= &sysfs_class_ops,
  };
  
  int sysdev_class_create_file(struct sysdev_class *c,
  			     struct sysdev_class_attribute *a)
  {
  	return sysfs_create_file(&c->kset.kobj, &a->attr);
  }
  EXPORT_SYMBOL_GPL(sysdev_class_create_file);
  
  void sysdev_class_remove_file(struct sysdev_class *c,
  			      struct sysdev_class_attribute *a)
  {
  	sysfs_remove_file(&c->kset.kobj, &a->attr);
  }
  EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
ca22e56de   Kay Sievers   driver-core: impl...
119
  extern struct kset *system_kset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120

60530afe1   Zhenwen Xu   Driver core: some...
121
  int sysdev_class_register(struct sysdev_class *cls)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
  {
9227c47bb   Dave Young   driver-core: make...
123
  	int retval;
838ea8e8d   Ben Dooks   sysdev: fix debug...
124
125
  	pr_debug("Registering sysdev class '%s'
  ", cls->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
  	INIT_LIST_HEAD(&cls->drivers);
ef79df263   Greg Kroah-Hartman   sysdev: fix probl...
127
  	memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
aade4041a   Greg Kroah-Hartman   kset: convert /sy...
128
  	cls->kset.kobj.parent = &system_kset->kobj;
3514faca1   Greg Kroah-Hartman   kobject: remove s...
129
  	cls->kset.kobj.ktype = &ktype_sysdev_class;
aade4041a   Greg Kroah-Hartman   kset: convert /sy...
130
  	cls->kset.kobj.kset = system_kset;
9227c47bb   Dave Young   driver-core: make...
131

acc0e90fb   Greg Kroah-Hartman   driver core: fix ...
132
  	retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name);
9227c47bb   Dave Young   driver-core: make...
133
134
  	if (retval)
  		return retval;
38457ab3a   Andi Kleen   sysfs: Add attrib...
135
136
137
138
139
  	retval = kset_register(&cls->kset);
  	if (!retval && cls->attrs)
  		retval = sysfs_create_files(&cls->kset.kobj,
  					    (const struct attribute **)cls->attrs);
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  }
60530afe1   Zhenwen Xu   Driver core: some...
141
  void sysdev_class_unregister(struct sysdev_class *cls)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
  {
  	pr_debug("Unregistering sysdev class '%s'
  ",
  		 kobject_name(&cls->kset.kobj));
38457ab3a   Andi Kleen   sysfs: Add attrib...
146
147
148
  	if (cls->attrs)
  		sysfs_remove_files(&cls->kset.kobj,
  				   (const struct attribute **)cls->attrs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
  	kset_unregister(&cls->kset);
  }
  
  EXPORT_SYMBOL_GPL(sysdev_class_register);
  EXPORT_SYMBOL_GPL(sysdev_class_unregister);
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
154
  static DEFINE_MUTEX(sysdev_drivers_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155

f4203e303   Borislav Petkov   sysdev: Do not re...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  /*
   * @dev != NULL means that we're unwinding because some drv->add()
   * failed for some reason. You need to grab sysdev_drivers_lock before
   * calling this.
   */
  static void __sysdev_driver_remove(struct sysdev_class *cls,
  				   struct sysdev_driver *drv,
  				   struct sys_device *from_dev)
  {
  	struct sys_device *dev = from_dev;
  
  	list_del_init(&drv->entry);
  	if (!cls)
  		return;
  
  	if (!drv->remove)
  		goto kset_put;
  
  	if (dev)
  		list_for_each_entry_continue_reverse(dev, &cls->kset.list,
  						     kobj.entry)
  			drv->remove(dev);
  	else
  		list_for_each_entry(dev, &cls->kset.list, kobj.entry)
  			drv->remove(dev);
  
  kset_put:
  	kset_put(&cls->kset);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  /**
25985edce   Lucas De Marchi   Fix common misspe...
186
   *	sysdev_driver_register - Register auxiliary driver
44b760a82   Akinobu Mita   sysdev: remove gl...
187
   *	@cls:	Device class driver belongs to.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
   *	@drv:	Driver.
   *
44b760a82   Akinobu Mita   sysdev: remove gl...
190
   *	@drv is inserted into @cls->drivers to be
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
   *	called on each operation on devices of that class. The refcount
   *	of @cls is incremented.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
   */
44b760a82   Akinobu Mita   sysdev: remove gl...
194
  int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  {
f4203e303   Borislav Petkov   sysdev: Do not re...
196
  	struct sys_device *dev = NULL;
44b760a82   Akinobu Mita   sysdev: remove gl...
197
  	int err = 0;
da009f39f   Ben Dooks   sysdev: detect mu...
198
  	if (!cls) {
345279bc1   Borislav Petkov   sysdev: Fixup war...
199
200
201
  		WARN(1, KERN_WARNING "sysdev: invalid class passed to %s!
  ",
  			__func__);
da009f39f   Ben Dooks   sysdev: detect mu...
202
203
204
205
  		return -EINVAL;
  	}
  
  	/* Check whether this driver has already been added to a class. */
f810a5cf2   Arjan van de Ven   Use WARN() in dri...
206
207
  	if (drv->entry.next && !list_empty(&drv->entry))
  		WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"
da009f39f   Ben Dooks   sysdev: detect mu...
208
209
210
  			" been registered to a class, something is wrong, but "
  			"will forge on!
  ", cls->name, drv);
da009f39f   Ben Dooks   sysdev: detect mu...
211

9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
212
  	mutex_lock(&sysdev_drivers_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
217
  	if (cls && kset_get(&cls->kset)) {
  		list_add_tail(&drv->entry, &cls->drivers);
  
  		/* If devices of this class already exist, tell the driver */
  		if (drv->add) {
f4203e303   Borislav Petkov   sysdev: Do not re...
218
219
220
221
222
  			list_for_each_entry(dev, &cls->kset.list, kobj.entry) {
  				err = drv->add(dev);
  				if (err)
  					goto unwind;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
  		}
44b760a82   Akinobu Mita   sysdev: remove gl...
224
225
  	} else {
  		err = -EINVAL;
f810a5cf2   Arjan van de Ven   Use WARN() in dri...
226
227
  		WARN(1, KERN_ERR "%s: invalid device class
  ", __func__);
44b760a82   Akinobu Mita   sysdev: remove gl...
228
  	}
f4203e303   Borislav Petkov   sysdev: Do not re...
229
230
231
232
233
234
235
  
  	goto unlock;
  
  unwind:
  	__sysdev_driver_remove(cls, drv, dev);
  
  unlock:
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
236
  	mutex_unlock(&sysdev_drivers_lock);
44b760a82   Akinobu Mita   sysdev: remove gl...
237
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
  /**
25985edce   Lucas De Marchi   Fix common misspe...
240
   *	sysdev_driver_unregister - Remove an auxiliary driver.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
   *	@cls:	Class driver belongs to.
   *	@drv:	Driver.
   */
60530afe1   Zhenwen Xu   Driver core: some...
244
245
  void sysdev_driver_unregister(struct sysdev_class *cls,
  			      struct sysdev_driver *drv)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  {
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
247
  	mutex_lock(&sysdev_drivers_lock);
f4203e303   Borislav Petkov   sysdev: Do not re...
248
  	__sysdev_driver_remove(cls, drv, NULL);
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
249
  	mutex_unlock(&sysdev_drivers_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
  EXPORT_SYMBOL_GPL(sysdev_driver_register);
  EXPORT_SYMBOL_GPL(sysdev_driver_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
  /**
   *	sysdev_register - add a system device to the tree
   *	@sysdev:	device in question
   *
   */
60530afe1   Zhenwen Xu   Driver core: some...
258
  int sysdev_register(struct sys_device *sysdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
  {
  	int error;
60530afe1   Zhenwen Xu   Driver core: some...
261
  	struct sysdev_class *cls = sysdev->cls;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
  
  	if (!cls)
  		return -EINVAL;
838ea8e8d   Ben Dooks   sysdev: fix debug...
265
266
267
  	pr_debug("Registering sys device of class '%s'
  ",
  		 kobject_name(&cls->kset.kobj));
61030bfb7   Greg Kroah-Hartman   Kobject: change d...
268

ef79df263   Greg Kroah-Hartman   sysdev: fix probl...
269
270
  	/* initialize the kobject to 0, in case it had previously been used */
  	memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
  	/* Make sure the kset is set */
  	sysdev->kobj.kset = &cls->kset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  	/* Register the object */
61030bfb7   Greg Kroah-Hartman   Kobject: change d...
274
275
276
  	error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
  				     "%s%d", kobject_name(&cls->kset.kobj),
  				     sysdev->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
  
  	if (!error) {
60530afe1   Zhenwen Xu   Driver core: some...
279
  		struct sysdev_driver *drv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280

838ea8e8d   Ben Dooks   sysdev: fix debug...
281
282
283
  		pr_debug("Registering sys device '%s'
  ",
  			 kobject_name(&sysdev->kobj));
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
284
  		mutex_lock(&sysdev_drivers_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
  		/* Generic notification is implicit, because it's that
  		 * code that should have called us.
  		 */
25985edce   Lucas De Marchi   Fix common misspe...
288
  		/* Notify class auxiliary drivers */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
  		list_for_each_entry(drv, &cls->drivers, entry) {
  			if (drv->add)
  				drv->add(sysdev);
  		}
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
293
  		mutex_unlock(&sysdev_drivers_lock);
79f0313bf   Xiaotian Feng   driver core: sysd...
294
  		kobject_uevent(&sysdev->kobj, KOBJ_ADD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
  	}
838ea8e8d   Ben Dooks   sysdev: fix debug...
296

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
  	return error;
  }
60530afe1   Zhenwen Xu   Driver core: some...
299
  void sysdev_unregister(struct sys_device *sysdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  {
60530afe1   Zhenwen Xu   Driver core: some...
301
  	struct sysdev_driver *drv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302

9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
303
  	mutex_lock(&sysdev_drivers_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
  	list_for_each_entry(drv, &sysdev->cls->drivers, entry) {
  		if (drv->remove)
  			drv->remove(sysdev);
  	}
9f3f776bd   Matthias Kaehlcke   sysdev: use mutex...
308
  	mutex_unlock(&sysdev_drivers_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309

c10997f65   Greg Kroah-Hartman   Kobject: convert ...
310
  	kobject_put(&sysdev->kobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  }
2e711c04d   Rafael J. Wysocki   PM: Remove sysdev...
312
313
  EXPORT_SYMBOL_GPL(sysdev_register);
  EXPORT_SYMBOL_GPL(sysdev_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314

9800794ac   Andi Kleen   sysdev: Add utili...
315
316
317
318
319
320
321
322
323
324
325
326
  #define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
  
  ssize_t sysdev_store_ulong(struct sys_device *sysdev,
  			   struct sysdev_attribute *attr,
  			   const char *buf, size_t size)
  {
  	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
  	char *end;
  	unsigned long new = simple_strtoul(buf, &end, 0);
  	if (end == buf)
  		return -EINVAL;
  	*(unsigned long *)(ea->var) = new;
4e318d7c6   Andi Kleen   sysfs: Fix return...
327
328
  	/* Always return full write size even if we didn't consume all */
  	return size;
9800794ac   Andi Kleen   sysdev: Add utili...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
  }
  EXPORT_SYMBOL_GPL(sysdev_store_ulong);
  
  ssize_t sysdev_show_ulong(struct sys_device *sysdev,
  			  struct sysdev_attribute *attr,
  			  char *buf)
  {
  	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
  	return snprintf(buf, PAGE_SIZE, "%lx
  ", *(unsigned long *)(ea->var));
  }
  EXPORT_SYMBOL_GPL(sysdev_show_ulong);
  
  ssize_t sysdev_store_int(struct sys_device *sysdev,
  			   struct sysdev_attribute *attr,
  			   const char *buf, size_t size)
  {
  	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
  	char *end;
  	long new = simple_strtol(buf, &end, 0);
  	if (end == buf || new > INT_MAX || new < INT_MIN)
  		return -EINVAL;
  	*(int *)(ea->var) = new;
4e318d7c6   Andi Kleen   sysfs: Fix return...
352
353
  	/* Always return full write size even if we didn't consume all */
  	return size;
9800794ac   Andi Kleen   sysdev: Add utili...
354
355
356
357
358
359
360
361
362
363
364
365
  }
  EXPORT_SYMBOL_GPL(sysdev_store_int);
  
  ssize_t sysdev_show_int(struct sys_device *sysdev,
  			  struct sysdev_attribute *attr,
  			  char *buf)
  {
  	struct sysdev_ext_attribute *ea = to_ext_attr(attr);
  	return snprintf(buf, PAGE_SIZE, "%d
  ", *(int *)(ea->var));
  }
  EXPORT_SYMBOL_GPL(sysdev_show_int);