Blame view

drivers/misc/enclosure.c 15.5 KB
d569d5bb3   James Bottomley   [SCSI] enclosure:...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  /*
   * Enclosure Services
   *
   * Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com>
   *
  **-----------------------------------------------------------------------------
  **
  **  This program is free software; you can redistribute it and/or
  **  modify it under the terms of the GNU General Public License
  **  version 2 as published by the Free Software Foundation.
  **
  **  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., 675 Mass Ave, Cambridge, MA 02139, USA.
  **
  **-----------------------------------------------------------------------------
  */
  #include <linux/device.h>
  #include <linux/enclosure.h>
  #include <linux/err.h>
  #include <linux/list.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
30
  #include <linux/slab.h>
d569d5bb3   James Bottomley   [SCSI] enclosure:...
31
32
33
34
  
  static LIST_HEAD(container_list);
  static DEFINE_MUTEX(container_list_lock);
  static struct class enclosure_class;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
35
36
  
  /**
163f52b6c   James Bottomley   [SCSI] ses: fix h...
37
38
39
   * enclosure_find - find an enclosure given a parent device
   * @dev:	the parent to match against
   * @start:	Optional enclosure device to start from (NULL if none)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
40
   *
163f52b6c   James Bottomley   [SCSI] ses: fix h...
41
42
43
44
45
46
47
48
49
50
51
52
   * Looks through the list of registered enclosures to find all those
   * with @dev as a parent.  Returns NULL if no enclosure is
   * found. @start can be used as a starting point to obtain multiple
   * enclosures per parent (should begin with NULL and then be set to
   * each returned enclosure device). Obtains a reference to the
   * enclosure class device which must be released with device_put().
   * If @start is not NULL, a reference must be taken on it which is
   * released before returning (this allows a loop through all
   * enclosures to exit with only the reference on the enclosure of
   * interest held).  Note that the @dev may correspond to the actual
   * device housing the enclosure, in which case no iteration via @start
   * is required.
d569d5bb3   James Bottomley   [SCSI] enclosure:...
53
   */
163f52b6c   James Bottomley   [SCSI] ses: fix h...
54
55
  struct enclosure_device *enclosure_find(struct device *dev,
  					struct enclosure_device *start)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
56
  {
ee959b00c   Tony Jones   SCSI: convert str...
57
  	struct enclosure_device *edev;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
58
59
  
  	mutex_lock(&container_list_lock);
163f52b6c   James Bottomley   [SCSI] ses: fix h...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  	edev = list_prepare_entry(start, &container_list, node);
  	if (start)
  		put_device(&start->edev);
  
  	list_for_each_entry_continue(edev, &container_list, node) {
  		struct device *parent = edev->edev.parent;
  		/* parent might not be immediate, so iterate up to
  		 * the root of the tree if necessary */
  		while (parent) {
  			if (parent == dev) {
  				get_device(&edev->edev);
  				mutex_unlock(&container_list_lock);
  				return edev;
  			}
  			parent = parent->parent;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  		}
  	}
  	mutex_unlock(&container_list_lock);
  
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(enclosure_find);
  
  /**
   * enclosure_for_each_device - calls a function for each enclosure
   * @fn:		the function to call
   * @data:	the data to pass to each call
   *
   * Loops over all the enclosures calling the function.
   *
   * Note, this function uses a mutex which will be held across calls to
   * @fn, so it must have non atomic context, and @fn may (although it
   * should not) sleep or otherwise cause the mutex to be held for
   * indefinite periods
   */
  int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *),
  			      void *data)
  {
  	int error = 0;
  	struct enclosure_device *edev;
  
  	mutex_lock(&container_list_lock);
  	list_for_each_entry(edev, &container_list, node) {
  		error = fn(edev, data);
  		if (error)
  			break;
  	}
  	mutex_unlock(&container_list_lock);
  
  	return error;
  }
  EXPORT_SYMBOL_GPL(enclosure_for_each_device);
  
  /**
   * enclosure_register - register device as an enclosure
   *
   * @dev:	device containing the enclosure
   * @components:	number of components in the enclosure
   *
   * This sets up the device for being an enclosure.  Note that @dev does
   * not have to be a dedicated enclosure device.  It may be some other type
   * of device that additionally responds to enclosure services
   */
  struct enclosure_device *
  enclosure_register(struct device *dev, const char *name, int components,
  		   struct enclosure_component_callbacks *cb)
  {
  	struct enclosure_device *edev =
  		kzalloc(sizeof(struct enclosure_device) +
  			sizeof(struct enclosure_component)*components,
  			GFP_KERNEL);
  	int err, i;
  
  	BUG_ON(!cb);
  
  	if (!edev)
  		return ERR_PTR(-ENOMEM);
  
  	edev->components = components;
ee959b00c   Tony Jones   SCSI: convert str...
139
140
  	edev->edev.class = &enclosure_class;
  	edev->edev.parent = get_device(dev);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
141
  	edev->cb = cb;
5e43754fd   Yinghai Lu   [SCSI] ses: fix p...
142
  	dev_set_name(&edev->edev, "%s", name);
ee959b00c   Tony Jones   SCSI: convert str...
143
  	err = device_register(&edev->edev);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
144
145
146
147
148
149
150
151
152
153
154
155
156
  	if (err)
  		goto err;
  
  	for (i = 0; i < components; i++)
  		edev->component[i].number = -1;
  
  	mutex_lock(&container_list_lock);
  	list_add_tail(&edev->node, &container_list);
  	mutex_unlock(&container_list_lock);
  
  	return edev;
  
   err:
ee959b00c   Tony Jones   SCSI: convert str...
157
  	put_device(edev->edev.parent);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  	kfree(edev);
  	return ERR_PTR(err);
  }
  EXPORT_SYMBOL_GPL(enclosure_register);
  
  static struct enclosure_component_callbacks enclosure_null_callbacks;
  
  /**
   * enclosure_unregister - remove an enclosure
   *
   * @edev:	the registered enclosure to remove;
   */
  void enclosure_unregister(struct enclosure_device *edev)
  {
  	int i;
  
  	mutex_lock(&container_list_lock);
  	list_del(&edev->node);
  	mutex_unlock(&container_list_lock);
  
  	for (i = 0; i < edev->components; i++)
  		if (edev->component[i].number != -1)
ee959b00c   Tony Jones   SCSI: convert str...
180
  			device_unregister(&edev->component[i].cdev);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
181
182
183
  
  	/* prevent any callbacks into service user */
  	edev->cb = &enclosure_null_callbacks;
ee959b00c   Tony Jones   SCSI: convert str...
184
  	device_unregister(&edev->edev);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
185
186
  }
  EXPORT_SYMBOL_GPL(enclosure_unregister);
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
187
188
189
190
191
  #define ENCLOSURE_NAME_SIZE	64
  
  static void enclosure_link_name(struct enclosure_component *cdev, char *name)
  {
  	strcpy(name, "enclosure_device:");
71610f55f   Kay Sievers   [SCSI] struct dev...
192
  	strcat(name, dev_name(&cdev->cdev));
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  }
  
  static void enclosure_remove_links(struct enclosure_component *cdev)
  {
  	char name[ENCLOSURE_NAME_SIZE];
  
  	enclosure_link_name(cdev, name);
  	sysfs_remove_link(&cdev->dev->kobj, name);
  	sysfs_remove_link(&cdev->cdev.kobj, "device");
  }
  
  static int enclosure_add_links(struct enclosure_component *cdev)
  {
  	int error;
  	char name[ENCLOSURE_NAME_SIZE];
  
  	error = sysfs_create_link(&cdev->cdev.kobj, &cdev->dev->kobj, "device");
  	if (error)
  		return error;
  
  	enclosure_link_name(cdev, name);
  	error = sysfs_create_link(&cdev->dev->kobj, &cdev->cdev.kobj, name);
  	if (error)
  		sysfs_remove_link(&cdev->cdev.kobj, "device");
  
  	return error;
  }
ee959b00c   Tony Jones   SCSI: convert str...
220
  static void enclosure_release(struct device *cdev)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
221
222
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev);
ee959b00c   Tony Jones   SCSI: convert str...
223
  	put_device(cdev->parent);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
224
225
  	kfree(edev);
  }
ee959b00c   Tony Jones   SCSI: convert str...
226
  static void enclosure_component_release(struct device *dev)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
227
  {
ee959b00c   Tony Jones   SCSI: convert str...
228
  	struct enclosure_component *cdev = to_enclosure_component(dev);
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
229
230
231
232
  	if (cdev->dev) {
  		enclosure_remove_links(cdev);
  		put_device(cdev->dev);
  	}
ee959b00c   Tony Jones   SCSI: convert str...
233
  	put_device(dev->parent);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
234
  }
a4dbd6740   David Brownell   driver model: con...
235
  static const struct attribute_group *enclosure_groups[];
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
236

d569d5bb3   James Bottomley   [SCSI] enclosure:...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  /**
   * enclosure_component_register - add a particular component to an enclosure
   * @edev:	the enclosure to add the component
   * @num:	the device number
   * @type:	the type of component being added
   * @name:	an optional name to appear in sysfs (leave NULL if none)
   *
   * Registers the component.  The name is optional for enclosures that
   * give their components a unique name.  If not, leave the field NULL
   * and a name will be assigned.
   *
   * Returns a pointer to the enclosure component or an error.
   */
  struct enclosure_component *
  enclosure_component_register(struct enclosure_device *edev,
  			     unsigned int number,
  			     enum enclosure_component_type type,
  			     const char *name)
  {
  	struct enclosure_component *ecomp;
ee959b00c   Tony Jones   SCSI: convert str...
257
  	struct device *cdev;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
258
259
260
261
262
263
264
265
266
267
268
269
270
  	int err;
  
  	if (number >= edev->components)
  		return ERR_PTR(-EINVAL);
  
  	ecomp = &edev->component[number];
  
  	if (ecomp->number != -1)
  		return ERR_PTR(-EINVAL);
  
  	ecomp->type = type;
  	ecomp->number = number;
  	cdev = &ecomp->cdev;
ee959b00c   Tony Jones   SCSI: convert str...
271
  	cdev->parent = get_device(&edev->edev);
5e43754fd   Yinghai Lu   [SCSI] ses: fix p...
272
273
  	if (name && name[0])
  		dev_set_name(cdev, "%s", name);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
274
  	else
71610f55f   Kay Sievers   [SCSI] struct dev...
275
  		dev_set_name(cdev, "%u", number);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
276

cb6b7f406   James Bottomley   [SCSI] ses: fix u...
277
278
  	cdev->release = enclosure_component_release;
  	cdev->groups = enclosure_groups;
ee959b00c   Tony Jones   SCSI: convert str...
279
  	err = device_register(cdev);
a91c1be21   James Bottomley   [SCSI] enclosure:...
280
281
282
283
284
  	if (err) {
  		ecomp->number = -1;
  		put_device(cdev);
  		return ERR_PTR(err);
  	}
d569d5bb3   James Bottomley   [SCSI] enclosure:...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  
  	return ecomp;
  }
  EXPORT_SYMBOL_GPL(enclosure_component_register);
  
  /**
   * enclosure_add_device - add a device as being part of an enclosure
   * @edev:	the enclosure device being added to.
   * @num:	the number of the component
   * @dev:	the device being added
   *
   * Declares a real device to reside in slot (or identifier) @num of an
   * enclosure.  This will cause the relevant sysfs links to appear.
   * This function may also be used to change a device associated with
   * an enclosure without having to call enclosure_remove_device() in
   * between.
   *
   * Returns zero on success or an error.
   */
  int enclosure_add_device(struct enclosure_device *edev, int component,
  			 struct device *dev)
  {
ee959b00c   Tony Jones   SCSI: convert str...
307
  	struct enclosure_component *cdev;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
308
309
310
  
  	if (!edev || component >= edev->components)
  		return -EINVAL;
ee959b00c   Tony Jones   SCSI: convert str...
311
  	cdev = &edev->component[component];
d569d5bb3   James Bottomley   [SCSI] enclosure:...
312

21fab1d05   James Bottomley   [SCSI] ses: updat...
313
314
  	if (cdev->dev == dev)
  		return -EEXIST;
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
315
316
  	if (cdev->dev)
  		enclosure_remove_links(cdev);
ee959b00c   Tony Jones   SCSI: convert str...
317
  	put_device(cdev->dev);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
318
  	cdev->dev = get_device(dev);
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
319
  	return enclosure_add_links(cdev);
d569d5bb3   James Bottomley   [SCSI] enclosure:...
320
321
322
323
324
325
326
327
328
329
330
  }
  EXPORT_SYMBOL_GPL(enclosure_add_device);
  
  /**
   * enclosure_remove_device - remove a device from an enclosure
   * @edev:	the enclosure device
   * @num:	the number of the component to remove
   *
   * Returns zero on success or an error.
   *
   */
43d8eb9cf   James Bottomley   [SCSI] ses: add s...
331
  int enclosure_remove_device(struct enclosure_device *edev, struct device *dev)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
332
  {
ee959b00c   Tony Jones   SCSI: convert str...
333
  	struct enclosure_component *cdev;
43d8eb9cf   James Bottomley   [SCSI] ses: add s...
334
  	int i;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
335

43d8eb9cf   James Bottomley   [SCSI] ses: add s...
336
  	if (!edev || !dev)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
337
  		return -EINVAL;
43d8eb9cf   James Bottomley   [SCSI] ses: add s...
338
339
340
341
342
343
344
345
346
347
348
  	for (i = 0; i < edev->components; i++) {
  		cdev = &edev->component[i];
  		if (cdev->dev == dev) {
  			enclosure_remove_links(cdev);
  			device_del(&cdev->cdev);
  			put_device(dev);
  			cdev->dev = NULL;
  			return device_add(&cdev->cdev);
  		}
  	}
  	return -ENODEV;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
349
350
351
352
353
354
  }
  EXPORT_SYMBOL_GPL(enclosure_remove_device);
  
  /*
   * sysfs pieces below
   */
ee959b00c   Tony Jones   SCSI: convert str...
355
356
357
  static ssize_t enclosure_show_components(struct device *cdev,
  					 struct device_attribute *attr,
  					 char *buf)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
358
359
360
361
362
363
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev);
  
  	return snprintf(buf, 40, "%d
  ", edev->components);
  }
ee959b00c   Tony Jones   SCSI: convert str...
364
  static struct device_attribute enclosure_attrs[] = {
d569d5bb3   James Bottomley   [SCSI] enclosure:...
365
366
367
368
369
370
371
  	__ATTR(components, S_IRUGO, enclosure_show_components, NULL),
  	__ATTR_NULL
  };
  
  static struct class enclosure_class = {
  	.name			= "enclosure",
  	.owner			= THIS_MODULE,
ee959b00c   Tony Jones   SCSI: convert str...
372
373
  	.dev_release		= enclosure_release,
  	.dev_attrs		= enclosure_attrs,
d569d5bb3   James Bottomley   [SCSI] enclosure:...
374
375
376
377
378
379
380
381
382
383
384
  };
  
  static const char *const enclosure_status [] = {
  	[ENCLOSURE_STATUS_UNSUPPORTED] = "unsupported",
  	[ENCLOSURE_STATUS_OK] = "OK",
  	[ENCLOSURE_STATUS_CRITICAL] = "critical",
  	[ENCLOSURE_STATUS_NON_CRITICAL] = "non-critical",
  	[ENCLOSURE_STATUS_UNRECOVERABLE] = "unrecoverable",
  	[ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed",
  	[ENCLOSURE_STATUS_UNKNOWN] = "unknown",
  	[ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable",
cc9b2e9f6   James Bottomley   [SCSI] enclosure:...
385
  	[ENCLOSURE_STATUS_MAX] = NULL,
d569d5bb3   James Bottomley   [SCSI] enclosure:...
386
387
388
389
390
391
  };
  
  static const char *const enclosure_type [] = {
  	[ENCLOSURE_COMPONENT_DEVICE] = "device",
  	[ENCLOSURE_COMPONENT_ARRAY_DEVICE] = "array device",
  };
ee959b00c   Tony Jones   SCSI: convert str...
392
393
  static ssize_t get_component_fault(struct device *cdev,
  				   struct device_attribute *attr, char *buf)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
394
395
396
397
398
399
400
401
402
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  
  	if (edev->cb->get_fault)
  		edev->cb->get_fault(edev, ecomp);
  	return snprintf(buf, 40, "%d
  ", ecomp->fault);
  }
ee959b00c   Tony Jones   SCSI: convert str...
403
404
405
  static ssize_t set_component_fault(struct device *cdev,
  				   struct device_attribute *attr,
  				   const char *buf, size_t count)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
406
407
408
409
410
411
412
413
414
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  	int val = simple_strtoul(buf, NULL, 0);
  
  	if (edev->cb->set_fault)
  		edev->cb->set_fault(edev, ecomp, val);
  	return count;
  }
ee959b00c   Tony Jones   SCSI: convert str...
415
416
  static ssize_t get_component_status(struct device *cdev,
  				    struct device_attribute *attr,char *buf)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
417
418
419
420
421
422
423
424
425
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  
  	if (edev->cb->get_status)
  		edev->cb->get_status(edev, ecomp);
  	return snprintf(buf, 40, "%s
  ", enclosure_status[ecomp->status]);
  }
ee959b00c   Tony Jones   SCSI: convert str...
426
427
428
  static ssize_t set_component_status(struct device *cdev,
  				    struct device_attribute *attr,
  				    const char *buf, size_t count)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  	int i;
  
  	for (i = 0; enclosure_status[i]; i++) {
  		if (strncmp(buf, enclosure_status[i],
  			    strlen(enclosure_status[i])) == 0 &&
  		    (buf[strlen(enclosure_status[i])] == '
  ' ||
  		     buf[strlen(enclosure_status[i])] == '\0'))
  			break;
  	}
  
  	if (enclosure_status[i] && edev->cb->set_status) {
  		edev->cb->set_status(edev, ecomp, i);
  		return count;
  	} else
  		return -EINVAL;
  }
ee959b00c   Tony Jones   SCSI: convert str...
449
450
  static ssize_t get_component_active(struct device *cdev,
  				    struct device_attribute *attr, char *buf)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
451
452
453
454
455
456
457
458
459
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  
  	if (edev->cb->get_active)
  		edev->cb->get_active(edev, ecomp);
  	return snprintf(buf, 40, "%d
  ", ecomp->active);
  }
ee959b00c   Tony Jones   SCSI: convert str...
460
461
462
  static ssize_t set_component_active(struct device *cdev,
  				    struct device_attribute *attr,
  				    const char *buf, size_t count)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
463
464
465
466
467
468
469
470
471
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  	int val = simple_strtoul(buf, NULL, 0);
  
  	if (edev->cb->set_active)
  		edev->cb->set_active(edev, ecomp, val);
  	return count;
  }
ee959b00c   Tony Jones   SCSI: convert str...
472
473
  static ssize_t get_component_locate(struct device *cdev,
  				    struct device_attribute *attr, char *buf)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
474
475
476
477
478
479
480
481
482
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  
  	if (edev->cb->get_locate)
  		edev->cb->get_locate(edev, ecomp);
  	return snprintf(buf, 40, "%d
  ", ecomp->locate);
  }
ee959b00c   Tony Jones   SCSI: convert str...
483
484
485
  static ssize_t set_component_locate(struct device *cdev,
  				    struct device_attribute *attr,
  				    const char *buf, size_t count)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
486
487
488
489
490
491
492
493
494
  {
  	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  	int val = simple_strtoul(buf, NULL, 0);
  
  	if (edev->cb->set_locate)
  		edev->cb->set_locate(edev, ecomp, val);
  	return count;
  }
ee959b00c   Tony Jones   SCSI: convert str...
495
496
  static ssize_t get_component_type(struct device *cdev,
  				  struct device_attribute *attr, char *buf)
d569d5bb3   James Bottomley   [SCSI] enclosure:...
497
498
499
500
501
502
  {
  	struct enclosure_component *ecomp = to_enclosure_component(cdev);
  
  	return snprintf(buf, 40, "%s
  ", enclosure_type[ecomp->type]);
  }
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
  static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault,
  		    set_component_fault);
  static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, get_component_status,
  		   set_component_status);
  static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active,
  		   set_component_active);
  static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate,
  		   set_component_locate);
  static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL);
  
  static struct attribute *enclosure_component_attrs[] = {
  	&dev_attr_fault.attr,
  	&dev_attr_status.attr,
  	&dev_attr_active.attr,
  	&dev_attr_locate.attr,
  	&dev_attr_type.attr,
  	NULL
d569d5bb3   James Bottomley   [SCSI] enclosure:...
520
  };
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
521
522
523
  static struct attribute_group enclosure_group = {
  	.attrs = enclosure_component_attrs,
  };
a4dbd6740   David Brownell   driver model: con...
524
  static const struct attribute_group *enclosure_groups[] = {
cb6b7f406   James Bottomley   [SCSI] ses: fix u...
525
526
  	&enclosure_group,
  	NULL
d569d5bb3   James Bottomley   [SCSI] enclosure:...
527
528
529
530
531
532
533
534
535
  };
  
  static int __init enclosure_init(void)
  {
  	int err;
  
  	err = class_register(&enclosure_class);
  	if (err)
  		return err;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
536
537
  
  	return 0;
d569d5bb3   James Bottomley   [SCSI] enclosure:...
538
539
540
541
  }
  
  static void __exit enclosure_exit(void)
  {
d569d5bb3   James Bottomley   [SCSI] enclosure:...
542
543
544
545
546
547
548
549
550
  	class_unregister(&enclosure_class);
  }
  
  module_init(enclosure_init);
  module_exit(enclosure_exit);
  
  MODULE_AUTHOR("James Bottomley");
  MODULE_DESCRIPTION("Enclosure Services");
  MODULE_LICENSE("GPL v2");