xref: /openbmc/linux/drivers/misc/enclosure.c (revision ee959b00c335d7780136c5abda37809191fe52c3)
1d569d5bbSJames Bottomley /*
2d569d5bbSJames Bottomley  * Enclosure Services
3d569d5bbSJames Bottomley  *
4d569d5bbSJames Bottomley  * Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com>
5d569d5bbSJames Bottomley  *
6d569d5bbSJames Bottomley **-----------------------------------------------------------------------------
7d569d5bbSJames Bottomley **
8d569d5bbSJames Bottomley **  This program is free software; you can redistribute it and/or
9d569d5bbSJames Bottomley **  modify it under the terms of the GNU General Public License
10d569d5bbSJames Bottomley **  version 2 as published by the Free Software Foundation.
11d569d5bbSJames Bottomley **
12d569d5bbSJames Bottomley **  This program is distributed in the hope that it will be useful,
13d569d5bbSJames Bottomley **  but WITHOUT ANY WARRANTY; without even the implied warranty of
14d569d5bbSJames Bottomley **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15d569d5bbSJames Bottomley **  GNU General Public License for more details.
16d569d5bbSJames Bottomley **
17d569d5bbSJames Bottomley **  You should have received a copy of the GNU General Public License
18d569d5bbSJames Bottomley **  along with this program; if not, write to the Free Software
19d569d5bbSJames Bottomley **  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20d569d5bbSJames Bottomley **
21d569d5bbSJames Bottomley **-----------------------------------------------------------------------------
22d569d5bbSJames Bottomley */
23d569d5bbSJames Bottomley #include <linux/device.h>
24d569d5bbSJames Bottomley #include <linux/enclosure.h>
25d569d5bbSJames Bottomley #include <linux/err.h>
26d569d5bbSJames Bottomley #include <linux/list.h>
27d569d5bbSJames Bottomley #include <linux/kernel.h>
28d569d5bbSJames Bottomley #include <linux/module.h>
29d569d5bbSJames Bottomley #include <linux/mutex.h>
30d569d5bbSJames Bottomley 
31d569d5bbSJames Bottomley static LIST_HEAD(container_list);
32d569d5bbSJames Bottomley static DEFINE_MUTEX(container_list_lock);
33d569d5bbSJames Bottomley static struct class enclosure_class;
34d569d5bbSJames Bottomley static struct class enclosure_component_class;
35d569d5bbSJames Bottomley 
36d569d5bbSJames Bottomley /**
37d569d5bbSJames Bottomley  * enclosure_find - find an enclosure given a device
38d569d5bbSJames Bottomley  * @dev:	the device to find for
39d569d5bbSJames Bottomley  *
40d569d5bbSJames Bottomley  * Looks through the list of registered enclosures to see
41d569d5bbSJames Bottomley  * if it can find a match for a device.  Returns NULL if no
42d569d5bbSJames Bottomley  * enclosure is found. Obtains a reference to the enclosure class
43*ee959b00STony Jones  * device which must be released with device_put().
44d569d5bbSJames Bottomley  */
45d569d5bbSJames Bottomley struct enclosure_device *enclosure_find(struct device *dev)
46d569d5bbSJames Bottomley {
47*ee959b00STony Jones 	struct enclosure_device *edev;
48d569d5bbSJames Bottomley 
49d569d5bbSJames Bottomley 	mutex_lock(&container_list_lock);
50d569d5bbSJames Bottomley 	list_for_each_entry(edev, &container_list, node) {
51*ee959b00STony Jones 		if (edev->edev.parent == dev) {
52*ee959b00STony Jones 			get_device(&edev->edev);
53d569d5bbSJames Bottomley 			mutex_unlock(&container_list_lock);
54d569d5bbSJames Bottomley 			return edev;
55d569d5bbSJames Bottomley 		}
56d569d5bbSJames Bottomley 	}
57d569d5bbSJames Bottomley 	mutex_unlock(&container_list_lock);
58d569d5bbSJames Bottomley 
59d569d5bbSJames Bottomley 	return NULL;
60d569d5bbSJames Bottomley }
61d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_find);
62d569d5bbSJames Bottomley 
63d569d5bbSJames Bottomley /**
64d569d5bbSJames Bottomley  * enclosure_for_each_device - calls a function for each enclosure
65d569d5bbSJames Bottomley  * @fn:		the function to call
66d569d5bbSJames Bottomley  * @data:	the data to pass to each call
67d569d5bbSJames Bottomley  *
68d569d5bbSJames Bottomley  * Loops over all the enclosures calling the function.
69d569d5bbSJames Bottomley  *
70d569d5bbSJames Bottomley  * Note, this function uses a mutex which will be held across calls to
71d569d5bbSJames Bottomley  * @fn, so it must have non atomic context, and @fn may (although it
72d569d5bbSJames Bottomley  * should not) sleep or otherwise cause the mutex to be held for
73d569d5bbSJames Bottomley  * indefinite periods
74d569d5bbSJames Bottomley  */
75d569d5bbSJames Bottomley int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *),
76d569d5bbSJames Bottomley 			      void *data)
77d569d5bbSJames Bottomley {
78d569d5bbSJames Bottomley 	int error = 0;
79d569d5bbSJames Bottomley 	struct enclosure_device *edev;
80d569d5bbSJames Bottomley 
81d569d5bbSJames Bottomley 	mutex_lock(&container_list_lock);
82d569d5bbSJames Bottomley 	list_for_each_entry(edev, &container_list, node) {
83d569d5bbSJames Bottomley 		error = fn(edev, data);
84d569d5bbSJames Bottomley 		if (error)
85d569d5bbSJames Bottomley 			break;
86d569d5bbSJames Bottomley 	}
87d569d5bbSJames Bottomley 	mutex_unlock(&container_list_lock);
88d569d5bbSJames Bottomley 
89d569d5bbSJames Bottomley 	return error;
90d569d5bbSJames Bottomley }
91d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_for_each_device);
92d569d5bbSJames Bottomley 
93d569d5bbSJames Bottomley /**
94d569d5bbSJames Bottomley  * enclosure_register - register device as an enclosure
95d569d5bbSJames Bottomley  *
96d569d5bbSJames Bottomley  * @dev:	device containing the enclosure
97d569d5bbSJames Bottomley  * @components:	number of components in the enclosure
98d569d5bbSJames Bottomley  *
99d569d5bbSJames Bottomley  * This sets up the device for being an enclosure.  Note that @dev does
100d569d5bbSJames Bottomley  * not have to be a dedicated enclosure device.  It may be some other type
101d569d5bbSJames Bottomley  * of device that additionally responds to enclosure services
102d569d5bbSJames Bottomley  */
103d569d5bbSJames Bottomley struct enclosure_device *
104d569d5bbSJames Bottomley enclosure_register(struct device *dev, const char *name, int components,
105d569d5bbSJames Bottomley 		   struct enclosure_component_callbacks *cb)
106d569d5bbSJames Bottomley {
107d569d5bbSJames Bottomley 	struct enclosure_device *edev =
108d569d5bbSJames Bottomley 		kzalloc(sizeof(struct enclosure_device) +
109d569d5bbSJames Bottomley 			sizeof(struct enclosure_component)*components,
110d569d5bbSJames Bottomley 			GFP_KERNEL);
111d569d5bbSJames Bottomley 	int err, i;
112d569d5bbSJames Bottomley 
113d569d5bbSJames Bottomley 	BUG_ON(!cb);
114d569d5bbSJames Bottomley 
115d569d5bbSJames Bottomley 	if (!edev)
116d569d5bbSJames Bottomley 		return ERR_PTR(-ENOMEM);
117d569d5bbSJames Bottomley 
118d569d5bbSJames Bottomley 	edev->components = components;
119d569d5bbSJames Bottomley 
120*ee959b00STony Jones 	edev->edev.class = &enclosure_class;
121*ee959b00STony Jones 	edev->edev.parent = get_device(dev);
122d569d5bbSJames Bottomley 	edev->cb = cb;
123*ee959b00STony Jones 	snprintf(edev->edev.bus_id, BUS_ID_SIZE, "%s", name);
124*ee959b00STony Jones 	err = device_register(&edev->edev);
125d569d5bbSJames Bottomley 	if (err)
126d569d5bbSJames Bottomley 		goto err;
127d569d5bbSJames Bottomley 
128d569d5bbSJames Bottomley 	for (i = 0; i < components; i++)
129d569d5bbSJames Bottomley 		edev->component[i].number = -1;
130d569d5bbSJames Bottomley 
131d569d5bbSJames Bottomley 	mutex_lock(&container_list_lock);
132d569d5bbSJames Bottomley 	list_add_tail(&edev->node, &container_list);
133d569d5bbSJames Bottomley 	mutex_unlock(&container_list_lock);
134d569d5bbSJames Bottomley 
135d569d5bbSJames Bottomley 	return edev;
136d569d5bbSJames Bottomley 
137d569d5bbSJames Bottomley  err:
138*ee959b00STony Jones 	put_device(edev->edev.parent);
139d569d5bbSJames Bottomley 	kfree(edev);
140d569d5bbSJames Bottomley 	return ERR_PTR(err);
141d569d5bbSJames Bottomley }
142d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_register);
143d569d5bbSJames Bottomley 
144d569d5bbSJames Bottomley static struct enclosure_component_callbacks enclosure_null_callbacks;
145d569d5bbSJames Bottomley 
146d569d5bbSJames Bottomley /**
147d569d5bbSJames Bottomley  * enclosure_unregister - remove an enclosure
148d569d5bbSJames Bottomley  *
149d569d5bbSJames Bottomley  * @edev:	the registered enclosure to remove;
150d569d5bbSJames Bottomley  */
151d569d5bbSJames Bottomley void enclosure_unregister(struct enclosure_device *edev)
152d569d5bbSJames Bottomley {
153d569d5bbSJames Bottomley 	int i;
154d569d5bbSJames Bottomley 
155d569d5bbSJames Bottomley 	mutex_lock(&container_list_lock);
156d569d5bbSJames Bottomley 	list_del(&edev->node);
157d569d5bbSJames Bottomley 	mutex_unlock(&container_list_lock);
158d569d5bbSJames Bottomley 
159d569d5bbSJames Bottomley 	for (i = 0; i < edev->components; i++)
160d569d5bbSJames Bottomley 		if (edev->component[i].number != -1)
161*ee959b00STony Jones 			device_unregister(&edev->component[i].cdev);
162d569d5bbSJames Bottomley 
163d569d5bbSJames Bottomley 	/* prevent any callbacks into service user */
164d569d5bbSJames Bottomley 	edev->cb = &enclosure_null_callbacks;
165*ee959b00STony Jones 	device_unregister(&edev->edev);
166d569d5bbSJames Bottomley }
167d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_unregister);
168d569d5bbSJames Bottomley 
169*ee959b00STony Jones static void enclosure_release(struct device *cdev)
170d569d5bbSJames Bottomley {
171d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev);
172d569d5bbSJames Bottomley 
173*ee959b00STony Jones 	put_device(cdev->parent);
174d569d5bbSJames Bottomley 	kfree(edev);
175d569d5bbSJames Bottomley }
176d569d5bbSJames Bottomley 
177*ee959b00STony Jones static void enclosure_component_release(struct device *dev)
178d569d5bbSJames Bottomley {
179*ee959b00STony Jones 	struct enclosure_component *cdev = to_enclosure_component(dev);
180*ee959b00STony Jones 
181d569d5bbSJames Bottomley 	put_device(cdev->dev);
182*ee959b00STony Jones 	put_device(dev->parent);
183d569d5bbSJames Bottomley }
184d569d5bbSJames Bottomley 
185d569d5bbSJames Bottomley /**
186d569d5bbSJames Bottomley  * enclosure_component_register - add a particular component to an enclosure
187d569d5bbSJames Bottomley  * @edev:	the enclosure to add the component
188d569d5bbSJames Bottomley  * @num:	the device number
189d569d5bbSJames Bottomley  * @type:	the type of component being added
190d569d5bbSJames Bottomley  * @name:	an optional name to appear in sysfs (leave NULL if none)
191d569d5bbSJames Bottomley  *
192d569d5bbSJames Bottomley  * Registers the component.  The name is optional for enclosures that
193d569d5bbSJames Bottomley  * give their components a unique name.  If not, leave the field NULL
194d569d5bbSJames Bottomley  * and a name will be assigned.
195d569d5bbSJames Bottomley  *
196d569d5bbSJames Bottomley  * Returns a pointer to the enclosure component or an error.
197d569d5bbSJames Bottomley  */
198d569d5bbSJames Bottomley struct enclosure_component *
199d569d5bbSJames Bottomley enclosure_component_register(struct enclosure_device *edev,
200d569d5bbSJames Bottomley 			     unsigned int number,
201d569d5bbSJames Bottomley 			     enum enclosure_component_type type,
202d569d5bbSJames Bottomley 			     const char *name)
203d569d5bbSJames Bottomley {
204d569d5bbSJames Bottomley 	struct enclosure_component *ecomp;
205*ee959b00STony Jones 	struct device *cdev;
206d569d5bbSJames Bottomley 	int err;
207d569d5bbSJames Bottomley 
208d569d5bbSJames Bottomley 	if (number >= edev->components)
209d569d5bbSJames Bottomley 		return ERR_PTR(-EINVAL);
210d569d5bbSJames Bottomley 
211d569d5bbSJames Bottomley 	ecomp = &edev->component[number];
212d569d5bbSJames Bottomley 
213d569d5bbSJames Bottomley 	if (ecomp->number != -1)
214d569d5bbSJames Bottomley 		return ERR_PTR(-EINVAL);
215d569d5bbSJames Bottomley 
216d569d5bbSJames Bottomley 	ecomp->type = type;
217d569d5bbSJames Bottomley 	ecomp->number = number;
218d569d5bbSJames Bottomley 	cdev = &ecomp->cdev;
219*ee959b00STony Jones 	cdev->parent = get_device(&edev->edev);
220d569d5bbSJames Bottomley 	cdev->class = &enclosure_component_class;
221d569d5bbSJames Bottomley 	if (name)
222*ee959b00STony Jones 		snprintf(cdev->bus_id, BUS_ID_SIZE, "%s", name);
223d569d5bbSJames Bottomley 	else
224*ee959b00STony Jones 		snprintf(cdev->bus_id, BUS_ID_SIZE, "%u", number);
225d569d5bbSJames Bottomley 
226*ee959b00STony Jones 	err = device_register(cdev);
227d569d5bbSJames Bottomley 	if (err)
228d569d5bbSJames Bottomley 		ERR_PTR(err);
229d569d5bbSJames Bottomley 
230d569d5bbSJames Bottomley 	return ecomp;
231d569d5bbSJames Bottomley }
232d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_component_register);
233d569d5bbSJames Bottomley 
234d569d5bbSJames Bottomley /**
235d569d5bbSJames Bottomley  * enclosure_add_device - add a device as being part of an enclosure
236d569d5bbSJames Bottomley  * @edev:	the enclosure device being added to.
237d569d5bbSJames Bottomley  * @num:	the number of the component
238d569d5bbSJames Bottomley  * @dev:	the device being added
239d569d5bbSJames Bottomley  *
240d569d5bbSJames Bottomley  * Declares a real device to reside in slot (or identifier) @num of an
241d569d5bbSJames Bottomley  * enclosure.  This will cause the relevant sysfs links to appear.
242d569d5bbSJames Bottomley  * This function may also be used to change a device associated with
243d569d5bbSJames Bottomley  * an enclosure without having to call enclosure_remove_device() in
244d569d5bbSJames Bottomley  * between.
245d569d5bbSJames Bottomley  *
246d569d5bbSJames Bottomley  * Returns zero on success or an error.
247d569d5bbSJames Bottomley  */
248d569d5bbSJames Bottomley int enclosure_add_device(struct enclosure_device *edev, int component,
249d569d5bbSJames Bottomley 			 struct device *dev)
250d569d5bbSJames Bottomley {
251*ee959b00STony Jones 	struct enclosure_component *cdev;
252d569d5bbSJames Bottomley 
253d569d5bbSJames Bottomley 	if (!edev || component >= edev->components)
254d569d5bbSJames Bottomley 		return -EINVAL;
255d569d5bbSJames Bottomley 
256*ee959b00STony Jones 	cdev = &edev->component[component];
257d569d5bbSJames Bottomley 
258*ee959b00STony Jones 	device_del(&cdev->cdev);
259d569d5bbSJames Bottomley 	put_device(cdev->dev);
260d569d5bbSJames Bottomley 	cdev->dev = get_device(dev);
261*ee959b00STony Jones 	return device_add(&cdev->cdev);
262d569d5bbSJames Bottomley }
263d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_add_device);
264d569d5bbSJames Bottomley 
265d569d5bbSJames Bottomley /**
266d569d5bbSJames Bottomley  * enclosure_remove_device - remove a device from an enclosure
267d569d5bbSJames Bottomley  * @edev:	the enclosure device
268d569d5bbSJames Bottomley  * @num:	the number of the component to remove
269d569d5bbSJames Bottomley  *
270d569d5bbSJames Bottomley  * Returns zero on success or an error.
271d569d5bbSJames Bottomley  *
272d569d5bbSJames Bottomley  */
273d569d5bbSJames Bottomley int enclosure_remove_device(struct enclosure_device *edev, int component)
274d569d5bbSJames Bottomley {
275*ee959b00STony Jones 	struct enclosure_component *cdev;
276d569d5bbSJames Bottomley 
277d569d5bbSJames Bottomley 	if (!edev || component >= edev->components)
278d569d5bbSJames Bottomley 		return -EINVAL;
279d569d5bbSJames Bottomley 
280*ee959b00STony Jones 	cdev = &edev->component[component];
281d569d5bbSJames Bottomley 
282*ee959b00STony Jones 	device_del(&cdev->cdev);
283d569d5bbSJames Bottomley 	put_device(cdev->dev);
284d569d5bbSJames Bottomley 	cdev->dev = NULL;
285*ee959b00STony Jones 	return device_add(&cdev->cdev);
286d569d5bbSJames Bottomley }
287d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_remove_device);
288d569d5bbSJames Bottomley 
289d569d5bbSJames Bottomley /*
290d569d5bbSJames Bottomley  * sysfs pieces below
291d569d5bbSJames Bottomley  */
292d569d5bbSJames Bottomley 
293*ee959b00STony Jones static ssize_t enclosure_show_components(struct device *cdev,
294*ee959b00STony Jones 					 struct device_attribute *attr,
295*ee959b00STony Jones 					 char *buf)
296d569d5bbSJames Bottomley {
297d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev);
298d569d5bbSJames Bottomley 
299d569d5bbSJames Bottomley 	return snprintf(buf, 40, "%d\n", edev->components);
300d569d5bbSJames Bottomley }
301d569d5bbSJames Bottomley 
302*ee959b00STony Jones static struct device_attribute enclosure_attrs[] = {
303d569d5bbSJames Bottomley 	__ATTR(components, S_IRUGO, enclosure_show_components, NULL),
304d569d5bbSJames Bottomley 	__ATTR_NULL
305d569d5bbSJames Bottomley };
306d569d5bbSJames Bottomley 
307d569d5bbSJames Bottomley static struct class enclosure_class = {
308d569d5bbSJames Bottomley 	.name			= "enclosure",
309d569d5bbSJames Bottomley 	.owner			= THIS_MODULE,
310*ee959b00STony Jones 	.dev_release		= enclosure_release,
311*ee959b00STony Jones 	.dev_attrs		= enclosure_attrs,
312d569d5bbSJames Bottomley };
313d569d5bbSJames Bottomley 
314d569d5bbSJames Bottomley static const char *const enclosure_status [] = {
315d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_UNSUPPORTED] = "unsupported",
316d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_OK] = "OK",
317d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_CRITICAL] = "critical",
318d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_NON_CRITICAL] = "non-critical",
319d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_UNRECOVERABLE] = "unrecoverable",
320d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed",
321d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_UNKNOWN] = "unknown",
322d569d5bbSJames Bottomley 	[ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable",
323d569d5bbSJames Bottomley };
324d569d5bbSJames Bottomley 
325d569d5bbSJames Bottomley static const char *const enclosure_type [] = {
326d569d5bbSJames Bottomley 	[ENCLOSURE_COMPONENT_DEVICE] = "device",
327d569d5bbSJames Bottomley 	[ENCLOSURE_COMPONENT_ARRAY_DEVICE] = "array device",
328d569d5bbSJames Bottomley };
329d569d5bbSJames Bottomley 
330*ee959b00STony Jones static ssize_t get_component_fault(struct device *cdev,
331*ee959b00STony Jones 				   struct device_attribute *attr, char *buf)
332d569d5bbSJames Bottomley {
333d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
334d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
335d569d5bbSJames Bottomley 
336d569d5bbSJames Bottomley 	if (edev->cb->get_fault)
337d569d5bbSJames Bottomley 		edev->cb->get_fault(edev, ecomp);
338d569d5bbSJames Bottomley 	return snprintf(buf, 40, "%d\n", ecomp->fault);
339d569d5bbSJames Bottomley }
340d569d5bbSJames Bottomley 
341*ee959b00STony Jones static ssize_t set_component_fault(struct device *cdev,
342*ee959b00STony Jones 				   struct device_attribute *attr,
343*ee959b00STony Jones 				   const char *buf, size_t count)
344d569d5bbSJames Bottomley {
345d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
346d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
347d569d5bbSJames Bottomley 	int val = simple_strtoul(buf, NULL, 0);
348d569d5bbSJames Bottomley 
349d569d5bbSJames Bottomley 	if (edev->cb->set_fault)
350d569d5bbSJames Bottomley 		edev->cb->set_fault(edev, ecomp, val);
351d569d5bbSJames Bottomley 	return count;
352d569d5bbSJames Bottomley }
353d569d5bbSJames Bottomley 
354*ee959b00STony Jones static ssize_t get_component_status(struct device *cdev,
355*ee959b00STony Jones 				    struct device_attribute *attr,char *buf)
356d569d5bbSJames Bottomley {
357d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
358d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
359d569d5bbSJames Bottomley 
360d569d5bbSJames Bottomley 	if (edev->cb->get_status)
361d569d5bbSJames Bottomley 		edev->cb->get_status(edev, ecomp);
362d569d5bbSJames Bottomley 	return snprintf(buf, 40, "%s\n", enclosure_status[ecomp->status]);
363d569d5bbSJames Bottomley }
364d569d5bbSJames Bottomley 
365*ee959b00STony Jones static ssize_t set_component_status(struct device *cdev,
366*ee959b00STony Jones 				    struct device_attribute *attr,
367*ee959b00STony Jones 				    const char *buf, size_t count)
368d569d5bbSJames Bottomley {
369d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
370d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
371d569d5bbSJames Bottomley 	int i;
372d569d5bbSJames Bottomley 
373d569d5bbSJames Bottomley 	for (i = 0; enclosure_status[i]; i++) {
374d569d5bbSJames Bottomley 		if (strncmp(buf, enclosure_status[i],
375d569d5bbSJames Bottomley 			    strlen(enclosure_status[i])) == 0 &&
376d569d5bbSJames Bottomley 		    (buf[strlen(enclosure_status[i])] == '\n' ||
377d569d5bbSJames Bottomley 		     buf[strlen(enclosure_status[i])] == '\0'))
378d569d5bbSJames Bottomley 			break;
379d569d5bbSJames Bottomley 	}
380d569d5bbSJames Bottomley 
381d569d5bbSJames Bottomley 	if (enclosure_status[i] && edev->cb->set_status) {
382d569d5bbSJames Bottomley 		edev->cb->set_status(edev, ecomp, i);
383d569d5bbSJames Bottomley 		return count;
384d569d5bbSJames Bottomley 	} else
385d569d5bbSJames Bottomley 		return -EINVAL;
386d569d5bbSJames Bottomley }
387d569d5bbSJames Bottomley 
388*ee959b00STony Jones static ssize_t get_component_active(struct device *cdev,
389*ee959b00STony Jones 				    struct device_attribute *attr, char *buf)
390d569d5bbSJames Bottomley {
391d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
392d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
393d569d5bbSJames Bottomley 
394d569d5bbSJames Bottomley 	if (edev->cb->get_active)
395d569d5bbSJames Bottomley 		edev->cb->get_active(edev, ecomp);
396d569d5bbSJames Bottomley 	return snprintf(buf, 40, "%d\n", ecomp->active);
397d569d5bbSJames Bottomley }
398d569d5bbSJames Bottomley 
399*ee959b00STony Jones static ssize_t set_component_active(struct device *cdev,
400*ee959b00STony Jones 				    struct device_attribute *attr,
401*ee959b00STony Jones 				    const char *buf, size_t count)
402d569d5bbSJames Bottomley {
403d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
404d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
405d569d5bbSJames Bottomley 	int val = simple_strtoul(buf, NULL, 0);
406d569d5bbSJames Bottomley 
407d569d5bbSJames Bottomley 	if (edev->cb->set_active)
408d569d5bbSJames Bottomley 		edev->cb->set_active(edev, ecomp, val);
409d569d5bbSJames Bottomley 	return count;
410d569d5bbSJames Bottomley }
411d569d5bbSJames Bottomley 
412*ee959b00STony Jones static ssize_t get_component_locate(struct device *cdev,
413*ee959b00STony Jones 				    struct device_attribute *attr, char *buf)
414d569d5bbSJames Bottomley {
415d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
416d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
417d569d5bbSJames Bottomley 
418d569d5bbSJames Bottomley 	if (edev->cb->get_locate)
419d569d5bbSJames Bottomley 		edev->cb->get_locate(edev, ecomp);
420d569d5bbSJames Bottomley 	return snprintf(buf, 40, "%d\n", ecomp->locate);
421d569d5bbSJames Bottomley }
422d569d5bbSJames Bottomley 
423*ee959b00STony Jones static ssize_t set_component_locate(struct device *cdev,
424*ee959b00STony Jones 				    struct device_attribute *attr,
425*ee959b00STony Jones 				    const char *buf, size_t count)
426d569d5bbSJames Bottomley {
427d569d5bbSJames Bottomley 	struct enclosure_device *edev = to_enclosure_device(cdev->parent);
428d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
429d569d5bbSJames Bottomley 	int val = simple_strtoul(buf, NULL, 0);
430d569d5bbSJames Bottomley 
431d569d5bbSJames Bottomley 	if (edev->cb->set_locate)
432d569d5bbSJames Bottomley 		edev->cb->set_locate(edev, ecomp, val);
433d569d5bbSJames Bottomley 	return count;
434d569d5bbSJames Bottomley }
435d569d5bbSJames Bottomley 
436*ee959b00STony Jones static ssize_t get_component_type(struct device *cdev,
437*ee959b00STony Jones 				  struct device_attribute *attr, char *buf)
438d569d5bbSJames Bottomley {
439d569d5bbSJames Bottomley 	struct enclosure_component *ecomp = to_enclosure_component(cdev);
440d569d5bbSJames Bottomley 
441d569d5bbSJames Bottomley 	return snprintf(buf, 40, "%s\n", enclosure_type[ecomp->type]);
442d569d5bbSJames Bottomley }
443d569d5bbSJames Bottomley 
444d569d5bbSJames Bottomley 
445*ee959b00STony Jones static struct device_attribute enclosure_component_attrs[] = {
446d569d5bbSJames Bottomley 	__ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault,
447d569d5bbSJames Bottomley 	       set_component_fault),
448d569d5bbSJames Bottomley 	__ATTR(status, S_IRUGO | S_IWUSR, get_component_status,
449d569d5bbSJames Bottomley 	       set_component_status),
450d569d5bbSJames Bottomley 	__ATTR(active, S_IRUGO | S_IWUSR, get_component_active,
451d569d5bbSJames Bottomley 	       set_component_active),
452d569d5bbSJames Bottomley 	__ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate,
453d569d5bbSJames Bottomley 	       set_component_locate),
454d569d5bbSJames Bottomley 	__ATTR(type, S_IRUGO, get_component_type, NULL),
455d569d5bbSJames Bottomley 	__ATTR_NULL
456d569d5bbSJames Bottomley };
457d569d5bbSJames Bottomley 
458d569d5bbSJames Bottomley static struct class enclosure_component_class =  {
459d569d5bbSJames Bottomley 	.name			= "enclosure_component",
460d569d5bbSJames Bottomley 	.owner			= THIS_MODULE,
461*ee959b00STony Jones 	.dev_attrs	= enclosure_component_attrs,
462*ee959b00STony Jones 	.dev_release		= enclosure_component_release,
463d569d5bbSJames Bottomley };
464d569d5bbSJames Bottomley 
465d569d5bbSJames Bottomley static int __init enclosure_init(void)
466d569d5bbSJames Bottomley {
467d569d5bbSJames Bottomley 	int err;
468d569d5bbSJames Bottomley 
469d569d5bbSJames Bottomley 	err = class_register(&enclosure_class);
470d569d5bbSJames Bottomley 	if (err)
471d569d5bbSJames Bottomley 		return err;
472d569d5bbSJames Bottomley 	err = class_register(&enclosure_component_class);
473d569d5bbSJames Bottomley 	if (err)
474d569d5bbSJames Bottomley 		goto err_out;
475d569d5bbSJames Bottomley 
476d569d5bbSJames Bottomley 	return 0;
477d569d5bbSJames Bottomley  err_out:
478d569d5bbSJames Bottomley 	class_unregister(&enclosure_class);
479d569d5bbSJames Bottomley 
480d569d5bbSJames Bottomley 	return err;
481d569d5bbSJames Bottomley }
482d569d5bbSJames Bottomley 
483d569d5bbSJames Bottomley static void __exit enclosure_exit(void)
484d569d5bbSJames Bottomley {
485d569d5bbSJames Bottomley 	class_unregister(&enclosure_component_class);
486d569d5bbSJames Bottomley 	class_unregister(&enclosure_class);
487d569d5bbSJames Bottomley }
488d569d5bbSJames Bottomley 
489d569d5bbSJames Bottomley module_init(enclosure_init);
490d569d5bbSJames Bottomley module_exit(enclosure_exit);
491d569d5bbSJames Bottomley 
492d569d5bbSJames Bottomley MODULE_AUTHOR("James Bottomley");
493d569d5bbSJames Bottomley MODULE_DESCRIPTION("Enclosure Services");
494d569d5bbSJames Bottomley MODULE_LICENSE("GPL v2");
495