182c29810SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d569d5bbSJames Bottomley /*
3d569d5bbSJames Bottomley * Enclosure Services
4d569d5bbSJames Bottomley *
5d569d5bbSJames Bottomley * Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com>
6d569d5bbSJames Bottomley *
7d569d5bbSJames Bottomley **-----------------------------------------------------------------------------
8d569d5bbSJames Bottomley **
9d569d5bbSJames Bottomley **
10d569d5bbSJames Bottomley **-----------------------------------------------------------------------------
11d569d5bbSJames Bottomley */
12d569d5bbSJames Bottomley #include <linux/device.h>
13d569d5bbSJames Bottomley #include <linux/enclosure.h>
14d569d5bbSJames Bottomley #include <linux/err.h>
15d569d5bbSJames Bottomley #include <linux/list.h>
16d569d5bbSJames Bottomley #include <linux/kernel.h>
17d569d5bbSJames Bottomley #include <linux/module.h>
18d569d5bbSJames Bottomley #include <linux/mutex.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
20d569d5bbSJames Bottomley
21d569d5bbSJames Bottomley static LIST_HEAD(container_list);
22d569d5bbSJames Bottomley static DEFINE_MUTEX(container_list_lock);
23d569d5bbSJames Bottomley static struct class enclosure_class;
24d569d5bbSJames Bottomley
25d569d5bbSJames Bottomley /**
26163f52b6SJames Bottomley * enclosure_find - find an enclosure given a parent device
27163f52b6SJames Bottomley * @dev: the parent to match against
28163f52b6SJames Bottomley * @start: Optional enclosure device to start from (NULL if none)
29d569d5bbSJames Bottomley *
30163f52b6SJames Bottomley * Looks through the list of registered enclosures to find all those
31163f52b6SJames Bottomley * with @dev as a parent. Returns NULL if no enclosure is
32163f52b6SJames Bottomley * found. @start can be used as a starting point to obtain multiple
33163f52b6SJames Bottomley * enclosures per parent (should begin with NULL and then be set to
34163f52b6SJames Bottomley * each returned enclosure device). Obtains a reference to the
35*f5b29c7aSMiaoqian Lin * enclosure class device which must be released with put_device().
36163f52b6SJames Bottomley * If @start is not NULL, a reference must be taken on it which is
37163f52b6SJames Bottomley * released before returning (this allows a loop through all
38163f52b6SJames Bottomley * enclosures to exit with only the reference on the enclosure of
39163f52b6SJames Bottomley * interest held). Note that the @dev may correspond to the actual
40163f52b6SJames Bottomley * device housing the enclosure, in which case no iteration via @start
41163f52b6SJames Bottomley * is required.
42d569d5bbSJames Bottomley */
enclosure_find(struct device * dev,struct enclosure_device * start)43163f52b6SJames Bottomley struct enclosure_device *enclosure_find(struct device *dev,
44163f52b6SJames Bottomley struct enclosure_device *start)
45d569d5bbSJames Bottomley {
46ee959b00STony Jones struct enclosure_device *edev;
47d569d5bbSJames Bottomley
48d569d5bbSJames Bottomley mutex_lock(&container_list_lock);
49163f52b6SJames Bottomley edev = list_prepare_entry(start, &container_list, node);
50163f52b6SJames Bottomley if (start)
51163f52b6SJames Bottomley put_device(&start->edev);
52163f52b6SJames Bottomley
53163f52b6SJames Bottomley list_for_each_entry_continue(edev, &container_list, node) {
54163f52b6SJames Bottomley struct device *parent = edev->edev.parent;
55163f52b6SJames Bottomley /* parent might not be immediate, so iterate up to
56163f52b6SJames Bottomley * the root of the tree if necessary */
57163f52b6SJames Bottomley while (parent) {
58163f52b6SJames Bottomley if (parent == dev) {
59ee959b00STony Jones get_device(&edev->edev);
60d569d5bbSJames Bottomley mutex_unlock(&container_list_lock);
61d569d5bbSJames Bottomley return edev;
62d569d5bbSJames Bottomley }
63163f52b6SJames Bottomley parent = parent->parent;
64163f52b6SJames Bottomley }
65d569d5bbSJames Bottomley }
66d569d5bbSJames Bottomley mutex_unlock(&container_list_lock);
67d569d5bbSJames Bottomley
68d569d5bbSJames Bottomley return NULL;
69d569d5bbSJames Bottomley }
70d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_find);
71d569d5bbSJames Bottomley
72d569d5bbSJames Bottomley /**
73d569d5bbSJames Bottomley * enclosure_for_each_device - calls a function for each enclosure
74d569d5bbSJames Bottomley * @fn: the function to call
75d569d5bbSJames Bottomley * @data: the data to pass to each call
76d569d5bbSJames Bottomley *
77d569d5bbSJames Bottomley * Loops over all the enclosures calling the function.
78d569d5bbSJames Bottomley *
79d569d5bbSJames Bottomley * Note, this function uses a mutex which will be held across calls to
80d569d5bbSJames Bottomley * @fn, so it must have non atomic context, and @fn may (although it
81d569d5bbSJames Bottomley * should not) sleep or otherwise cause the mutex to be held for
82d569d5bbSJames Bottomley * indefinite periods
83d569d5bbSJames Bottomley */
enclosure_for_each_device(int (* fn)(struct enclosure_device *,void *),void * data)84d569d5bbSJames Bottomley int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *),
85d569d5bbSJames Bottomley void *data)
86d569d5bbSJames Bottomley {
87d569d5bbSJames Bottomley int error = 0;
88d569d5bbSJames Bottomley struct enclosure_device *edev;
89d569d5bbSJames Bottomley
90d569d5bbSJames Bottomley mutex_lock(&container_list_lock);
91d569d5bbSJames Bottomley list_for_each_entry(edev, &container_list, node) {
92d569d5bbSJames Bottomley error = fn(edev, data);
93d569d5bbSJames Bottomley if (error)
94d569d5bbSJames Bottomley break;
95d569d5bbSJames Bottomley }
96d569d5bbSJames Bottomley mutex_unlock(&container_list_lock);
97d569d5bbSJames Bottomley
98d569d5bbSJames Bottomley return error;
99d569d5bbSJames Bottomley }
100d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_for_each_device);
101d569d5bbSJames Bottomley
102d569d5bbSJames Bottomley /**
103d569d5bbSJames Bottomley * enclosure_register - register device as an enclosure
104d569d5bbSJames Bottomley *
105d569d5bbSJames Bottomley * @dev: device containing the enclosure
10682f5b473SLee Jones * @name: chosen device name
107d569d5bbSJames Bottomley * @components: number of components in the enclosure
10882f5b473SLee Jones * @cb: platform call-backs
109d569d5bbSJames Bottomley *
110d569d5bbSJames Bottomley * This sets up the device for being an enclosure. Note that @dev does
111d569d5bbSJames Bottomley * not have to be a dedicated enclosure device. It may be some other type
112d569d5bbSJames Bottomley * of device that additionally responds to enclosure services
113d569d5bbSJames Bottomley */
114d569d5bbSJames Bottomley struct enclosure_device *
enclosure_register(struct device * dev,const char * name,int components,struct enclosure_component_callbacks * cb)115d569d5bbSJames Bottomley enclosure_register(struct device *dev, const char *name, int components,
116d569d5bbSJames Bottomley struct enclosure_component_callbacks *cb)
117d569d5bbSJames Bottomley {
118d569d5bbSJames Bottomley struct enclosure_device *edev =
119e3575c12SGustavo A. R. Silva kzalloc(struct_size(edev, component, components), GFP_KERNEL);
120d569d5bbSJames Bottomley int err, i;
121d569d5bbSJames Bottomley
122d569d5bbSJames Bottomley BUG_ON(!cb);
123d569d5bbSJames Bottomley
124d569d5bbSJames Bottomley if (!edev)
125d569d5bbSJames Bottomley return ERR_PTR(-ENOMEM);
126d569d5bbSJames Bottomley
127d569d5bbSJames Bottomley edev->components = components;
128d569d5bbSJames Bottomley
129ee959b00STony Jones edev->edev.class = &enclosure_class;
130ee959b00STony Jones edev->edev.parent = get_device(dev);
131d569d5bbSJames Bottomley edev->cb = cb;
1325e43754fSYinghai Lu dev_set_name(&edev->edev, "%s", name);
133ee959b00STony Jones err = device_register(&edev->edev);
134d569d5bbSJames Bottomley if (err)
135d569d5bbSJames Bottomley goto err;
136d569d5bbSJames Bottomley
137921ce7f5SDan Williams for (i = 0; i < components; i++) {
138d569d5bbSJames Bottomley edev->component[i].number = -1;
139921ce7f5SDan Williams edev->component[i].slot = -1;
14075106523SMauricio Faria de Oliveira edev->component[i].power_status = -1;
141921ce7f5SDan Williams }
142d569d5bbSJames Bottomley
143d569d5bbSJames Bottomley mutex_lock(&container_list_lock);
144d569d5bbSJames Bottomley list_add_tail(&edev->node, &container_list);
145d569d5bbSJames Bottomley mutex_unlock(&container_list_lock);
146d569d5bbSJames Bottomley
147d569d5bbSJames Bottomley return edev;
148d569d5bbSJames Bottomley
149d569d5bbSJames Bottomley err:
150ee959b00STony Jones put_device(edev->edev.parent);
151d569d5bbSJames Bottomley kfree(edev);
152d569d5bbSJames Bottomley return ERR_PTR(err);
153d569d5bbSJames Bottomley }
154d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_register);
155d569d5bbSJames Bottomley
156d569d5bbSJames Bottomley static struct enclosure_component_callbacks enclosure_null_callbacks;
157d569d5bbSJames Bottomley
158d569d5bbSJames Bottomley /**
159d569d5bbSJames Bottomley * enclosure_unregister - remove an enclosure
160d569d5bbSJames Bottomley *
161d569d5bbSJames Bottomley * @edev: the registered enclosure to remove;
162d569d5bbSJames Bottomley */
enclosure_unregister(struct enclosure_device * edev)163d569d5bbSJames Bottomley void enclosure_unregister(struct enclosure_device *edev)
164d569d5bbSJames Bottomley {
165d569d5bbSJames Bottomley int i;
166d569d5bbSJames Bottomley
167d569d5bbSJames Bottomley mutex_lock(&container_list_lock);
168d569d5bbSJames Bottomley list_del(&edev->node);
169d569d5bbSJames Bottomley mutex_unlock(&container_list_lock);
170d569d5bbSJames Bottomley
171d569d5bbSJames Bottomley for (i = 0; i < edev->components; i++)
172d569d5bbSJames Bottomley if (edev->component[i].number != -1)
173ee959b00STony Jones device_unregister(&edev->component[i].cdev);
174d569d5bbSJames Bottomley
175d569d5bbSJames Bottomley /* prevent any callbacks into service user */
176d569d5bbSJames Bottomley edev->cb = &enclosure_null_callbacks;
177ee959b00STony Jones device_unregister(&edev->edev);
178d569d5bbSJames Bottomley }
179d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_unregister);
180d569d5bbSJames Bottomley
181cb6b7f40SJames Bottomley #define ENCLOSURE_NAME_SIZE 64
182d2fd76e6SMarkus Stockhausen #define COMPONENT_NAME_SIZE 64
183cb6b7f40SJames Bottomley
enclosure_link_name(struct enclosure_component * cdev,char * name)184cb6b7f40SJames Bottomley static void enclosure_link_name(struct enclosure_component *cdev, char *name)
185cb6b7f40SJames Bottomley {
186cb6b7f40SJames Bottomley strcpy(name, "enclosure_device:");
18771610f55SKay Sievers strcat(name, dev_name(&cdev->cdev));
188cb6b7f40SJames Bottomley }
189cb6b7f40SJames Bottomley
enclosure_remove_links(struct enclosure_component * cdev)190cb6b7f40SJames Bottomley static void enclosure_remove_links(struct enclosure_component *cdev)
191cb6b7f40SJames Bottomley {
192cb6b7f40SJames Bottomley char name[ENCLOSURE_NAME_SIZE];
193cb6b7f40SJames Bottomley
19411e52a69SJames Bottomley enclosure_link_name(cdev, name);
19511e52a69SJames Bottomley
196a1470c7bSJames Bottomley /*
197a1470c7bSJames Bottomley * In odd circumstances, like multipath devices, something else may
198a1470c7bSJames Bottomley * already have removed the links, so check for this condition first.
199a1470c7bSJames Bottomley */
20011e52a69SJames Bottomley if (cdev->dev->kobj.sd)
201cb6b7f40SJames Bottomley sysfs_remove_link(&cdev->dev->kobj, name);
20211e52a69SJames Bottomley
20311e52a69SJames Bottomley if (cdev->cdev.kobj.sd)
204cb6b7f40SJames Bottomley sysfs_remove_link(&cdev->cdev.kobj, "device");
205cb6b7f40SJames Bottomley }
206cb6b7f40SJames Bottomley
enclosure_add_links(struct enclosure_component * cdev)207cb6b7f40SJames Bottomley static int enclosure_add_links(struct enclosure_component *cdev)
208cb6b7f40SJames Bottomley {
209cb6b7f40SJames Bottomley int error;
210cb6b7f40SJames Bottomley char name[ENCLOSURE_NAME_SIZE];
211cb6b7f40SJames Bottomley
212cb6b7f40SJames Bottomley error = sysfs_create_link(&cdev->cdev.kobj, &cdev->dev->kobj, "device");
213cb6b7f40SJames Bottomley if (error)
214cb6b7f40SJames Bottomley return error;
215cb6b7f40SJames Bottomley
216cb6b7f40SJames Bottomley enclosure_link_name(cdev, name);
217cb6b7f40SJames Bottomley error = sysfs_create_link(&cdev->dev->kobj, &cdev->cdev.kobj, name);
218cb6b7f40SJames Bottomley if (error)
219cb6b7f40SJames Bottomley sysfs_remove_link(&cdev->cdev.kobj, "device");
220cb6b7f40SJames Bottomley
221cb6b7f40SJames Bottomley return error;
222cb6b7f40SJames Bottomley }
223cb6b7f40SJames Bottomley
enclosure_release(struct device * cdev)224ee959b00STony Jones static void enclosure_release(struct device *cdev)
225d569d5bbSJames Bottomley {
226d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev);
227d569d5bbSJames Bottomley
228ee959b00STony Jones put_device(cdev->parent);
229d569d5bbSJames Bottomley kfree(edev);
230d569d5bbSJames Bottomley }
231d569d5bbSJames Bottomley
enclosure_component_release(struct device * dev)232ee959b00STony Jones static void enclosure_component_release(struct device *dev)
233d569d5bbSJames Bottomley {
234ee959b00STony Jones struct enclosure_component *cdev = to_enclosure_component(dev);
235ee959b00STony Jones
236cb6b7f40SJames Bottomley if (cdev->dev) {
237cb6b7f40SJames Bottomley enclosure_remove_links(cdev);
238d569d5bbSJames Bottomley put_device(cdev->dev);
239cb6b7f40SJames Bottomley }
240ee959b00STony Jones put_device(dev->parent);
241d569d5bbSJames Bottomley }
242d569d5bbSJames Bottomley
243d2fd76e6SMarkus Stockhausen static struct enclosure_component *
enclosure_component_find_by_name(struct enclosure_device * edev,const char * name)244d2fd76e6SMarkus Stockhausen enclosure_component_find_by_name(struct enclosure_device *edev,
245d2fd76e6SMarkus Stockhausen const char *name)
246d2fd76e6SMarkus Stockhausen {
247d2fd76e6SMarkus Stockhausen int i;
248d2fd76e6SMarkus Stockhausen const char *cname;
249d2fd76e6SMarkus Stockhausen struct enclosure_component *ecomp;
250d2fd76e6SMarkus Stockhausen
251d2fd76e6SMarkus Stockhausen if (!edev || !name || !name[0])
252d2fd76e6SMarkus Stockhausen return NULL;
253d2fd76e6SMarkus Stockhausen
254d2fd76e6SMarkus Stockhausen for (i = 0; i < edev->components; i++) {
255d2fd76e6SMarkus Stockhausen ecomp = &edev->component[i];
256d2fd76e6SMarkus Stockhausen cname = dev_name(&ecomp->cdev);
257d2fd76e6SMarkus Stockhausen if (ecomp->number != -1 &&
258d2fd76e6SMarkus Stockhausen cname && cname[0] &&
259d2fd76e6SMarkus Stockhausen !strcmp(cname, name))
260d2fd76e6SMarkus Stockhausen return ecomp;
261d2fd76e6SMarkus Stockhausen }
262d2fd76e6SMarkus Stockhausen
263d2fd76e6SMarkus Stockhausen return NULL;
264d2fd76e6SMarkus Stockhausen }
265d2fd76e6SMarkus Stockhausen
266899826f1SGreg Kroah-Hartman static const struct attribute_group *enclosure_component_groups[];
267cb6b7f40SJames Bottomley
268d569d5bbSJames Bottomley /**
269ed09dcc8SDan Williams * enclosure_component_alloc - prepare a new enclosure component
270d569d5bbSJames Bottomley * @edev: the enclosure to add the component
27182f5b473SLee Jones * @number: the device number
272d569d5bbSJames Bottomley * @type: the type of component being added
273d569d5bbSJames Bottomley * @name: an optional name to appear in sysfs (leave NULL if none)
274d569d5bbSJames Bottomley *
275ed09dcc8SDan Williams * The name is optional for enclosures that give their components a unique
276ed09dcc8SDan Williams * name. If not, leave the field NULL and a name will be assigned.
277d569d5bbSJames Bottomley *
278d569d5bbSJames Bottomley * Returns a pointer to the enclosure component or an error.
279d569d5bbSJames Bottomley */
280d569d5bbSJames Bottomley struct enclosure_component *
enclosure_component_alloc(struct enclosure_device * edev,unsigned int number,enum enclosure_component_type type,const char * name)281ed09dcc8SDan Williams enclosure_component_alloc(struct enclosure_device *edev,
282d569d5bbSJames Bottomley unsigned int number,
283d569d5bbSJames Bottomley enum enclosure_component_type type,
284d569d5bbSJames Bottomley const char *name)
285d569d5bbSJames Bottomley {
286d569d5bbSJames Bottomley struct enclosure_component *ecomp;
287ee959b00STony Jones struct device *cdev;
288ed09dcc8SDan Williams int i;
289d2fd76e6SMarkus Stockhausen char newname[COMPONENT_NAME_SIZE];
290d569d5bbSJames Bottomley
291d569d5bbSJames Bottomley if (number >= edev->components)
292d569d5bbSJames Bottomley return ERR_PTR(-EINVAL);
293d569d5bbSJames Bottomley
294d569d5bbSJames Bottomley ecomp = &edev->component[number];
295d569d5bbSJames Bottomley
296d569d5bbSJames Bottomley if (ecomp->number != -1)
297d569d5bbSJames Bottomley return ERR_PTR(-EINVAL);
298d569d5bbSJames Bottomley
299d569d5bbSJames Bottomley ecomp->type = type;
300d569d5bbSJames Bottomley ecomp->number = number;
301d569d5bbSJames Bottomley cdev = &ecomp->cdev;
302ee959b00STony Jones cdev->parent = get_device(&edev->edev);
303d2fd76e6SMarkus Stockhausen
304d2fd76e6SMarkus Stockhausen if (name && name[0]) {
305d2fd76e6SMarkus Stockhausen /* Some hardware (e.g. enclosure in RX300 S6) has components
306d2fd76e6SMarkus Stockhausen * with non unique names. Registering duplicates in sysfs
307d2fd76e6SMarkus Stockhausen * will lead to warnings during bootup. So make the names
308d2fd76e6SMarkus Stockhausen * unique by appending consecutive numbers -1, -2, ... */
309d2fd76e6SMarkus Stockhausen i = 1;
310d2fd76e6SMarkus Stockhausen snprintf(newname, COMPONENT_NAME_SIZE,
311d2fd76e6SMarkus Stockhausen "%s", name);
312d2fd76e6SMarkus Stockhausen while (enclosure_component_find_by_name(edev, newname))
313d2fd76e6SMarkus Stockhausen snprintf(newname, COMPONENT_NAME_SIZE,
314d2fd76e6SMarkus Stockhausen "%s-%i", name, i++);
315d2fd76e6SMarkus Stockhausen dev_set_name(cdev, "%s", newname);
316d2fd76e6SMarkus Stockhausen } else
31771610f55SKay Sievers dev_set_name(cdev, "%u", number);
318d569d5bbSJames Bottomley
319cb6b7f40SJames Bottomley cdev->release = enclosure_component_release;
320899826f1SGreg Kroah-Hartman cdev->groups = enclosure_component_groups;
321cb6b7f40SJames Bottomley
322ed09dcc8SDan Williams return ecomp;
323ed09dcc8SDan Williams }
324ed09dcc8SDan Williams EXPORT_SYMBOL_GPL(enclosure_component_alloc);
325ed09dcc8SDan Williams
326ed09dcc8SDan Williams /**
327ed09dcc8SDan Williams * enclosure_component_register - publishes an initialized enclosure component
328ed09dcc8SDan Williams * @ecomp: component to add
329ed09dcc8SDan Williams *
330ed09dcc8SDan Williams * Returns 0 on successful registration, releases the component otherwise
331ed09dcc8SDan Williams */
enclosure_component_register(struct enclosure_component * ecomp)332ed09dcc8SDan Williams int enclosure_component_register(struct enclosure_component *ecomp)
333ed09dcc8SDan Williams {
334ed09dcc8SDan Williams struct device *cdev;
335ed09dcc8SDan Williams int err;
336ed09dcc8SDan Williams
337ed09dcc8SDan Williams cdev = &ecomp->cdev;
338ee959b00STony Jones err = device_register(cdev);
339a91c1be2SJames Bottomley if (err) {
340a91c1be2SJames Bottomley ecomp->number = -1;
341a91c1be2SJames Bottomley put_device(cdev);
342ed09dcc8SDan Williams return err;
343a91c1be2SJames Bottomley }
344d569d5bbSJames Bottomley
345ed09dcc8SDan Williams return 0;
346d569d5bbSJames Bottomley }
347d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_component_register);
348d569d5bbSJames Bottomley
349d569d5bbSJames Bottomley /**
350d569d5bbSJames Bottomley * enclosure_add_device - add a device as being part of an enclosure
351d569d5bbSJames Bottomley * @edev: the enclosure device being added to.
35282f5b473SLee Jones * @component: the number of the component
353d569d5bbSJames Bottomley * @dev: the device being added
354d569d5bbSJames Bottomley *
355d569d5bbSJames Bottomley * Declares a real device to reside in slot (or identifier) @num of an
356d569d5bbSJames Bottomley * enclosure. This will cause the relevant sysfs links to appear.
357d569d5bbSJames Bottomley * This function may also be used to change a device associated with
358d569d5bbSJames Bottomley * an enclosure without having to call enclosure_remove_device() in
359d569d5bbSJames Bottomley * between.
360d569d5bbSJames Bottomley *
361d569d5bbSJames Bottomley * Returns zero on success or an error.
362d569d5bbSJames Bottomley */
enclosure_add_device(struct enclosure_device * edev,int component,struct device * dev)363d569d5bbSJames Bottomley int enclosure_add_device(struct enclosure_device *edev, int component,
364d569d5bbSJames Bottomley struct device *dev)
365d569d5bbSJames Bottomley {
366ee959b00STony Jones struct enclosure_component *cdev;
36762e62ffdSMaurizio Lombardi int err;
368d569d5bbSJames Bottomley
369d569d5bbSJames Bottomley if (!edev || component >= edev->components)
370d569d5bbSJames Bottomley return -EINVAL;
371d569d5bbSJames Bottomley
372ee959b00STony Jones cdev = &edev->component[component];
373d569d5bbSJames Bottomley
37421fab1d0SJames Bottomley if (cdev->dev == dev)
37521fab1d0SJames Bottomley return -EEXIST;
37621fab1d0SJames Bottomley
37762e62ffdSMaurizio Lombardi if (cdev->dev) {
378cb6b7f40SJames Bottomley enclosure_remove_links(cdev);
379d569d5bbSJames Bottomley put_device(cdev->dev);
38062e62ffdSMaurizio Lombardi }
381d569d5bbSJames Bottomley cdev->dev = get_device(dev);
38262e62ffdSMaurizio Lombardi err = enclosure_add_links(cdev);
38362e62ffdSMaurizio Lombardi if (err) {
38462e62ffdSMaurizio Lombardi put_device(cdev->dev);
38562e62ffdSMaurizio Lombardi cdev->dev = NULL;
38662e62ffdSMaurizio Lombardi }
38762e62ffdSMaurizio Lombardi return err;
388d569d5bbSJames Bottomley }
389d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_add_device);
390d569d5bbSJames Bottomley
391d569d5bbSJames Bottomley /**
392d569d5bbSJames Bottomley * enclosure_remove_device - remove a device from an enclosure
393d569d5bbSJames Bottomley * @edev: the enclosure device
3946a57251cSLee Jones * @dev: device to remove/put
395d569d5bbSJames Bottomley *
396d569d5bbSJames Bottomley * Returns zero on success or an error.
397d569d5bbSJames Bottomley *
398d569d5bbSJames Bottomley */
enclosure_remove_device(struct enclosure_device * edev,struct device * dev)39943d8eb9cSJames Bottomley int enclosure_remove_device(struct enclosure_device *edev, struct device *dev)
400d569d5bbSJames Bottomley {
401ee959b00STony Jones struct enclosure_component *cdev;
40243d8eb9cSJames Bottomley int i;
403d569d5bbSJames Bottomley
40443d8eb9cSJames Bottomley if (!edev || !dev)
405d569d5bbSJames Bottomley return -EINVAL;
406d569d5bbSJames Bottomley
40743d8eb9cSJames Bottomley for (i = 0; i < edev->components; i++) {
40843d8eb9cSJames Bottomley cdev = &edev->component[i];
40943d8eb9cSJames Bottomley if (cdev->dev == dev) {
41043d8eb9cSJames Bottomley enclosure_remove_links(cdev);
41143d8eb9cSJames Bottomley put_device(dev);
412d569d5bbSJames Bottomley cdev->dev = NULL;
413529244bdSJames Bottomley return 0;
414d569d5bbSJames Bottomley }
41543d8eb9cSJames Bottomley }
41643d8eb9cSJames Bottomley return -ENODEV;
41743d8eb9cSJames Bottomley }
418d569d5bbSJames Bottomley EXPORT_SYMBOL_GPL(enclosure_remove_device);
419d569d5bbSJames Bottomley
420d569d5bbSJames Bottomley /*
421d569d5bbSJames Bottomley * sysfs pieces below
422d569d5bbSJames Bottomley */
423d569d5bbSJames Bottomley
components_show(struct device * cdev,struct device_attribute * attr,char * buf)424899826f1SGreg Kroah-Hartman static ssize_t components_show(struct device *cdev,
425899826f1SGreg Kroah-Hartman struct device_attribute *attr, char *buf)
426d569d5bbSJames Bottomley {
427d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev);
428d569d5bbSJames Bottomley
429714f1af1SYe Guojin return sysfs_emit(buf, "%d\n", edev->components);
430d569d5bbSJames Bottomley }
431899826f1SGreg Kroah-Hartman static DEVICE_ATTR_RO(components);
432d569d5bbSJames Bottomley
id_show(struct device * cdev,struct device_attribute * attr,char * buf)433967f7babSDan Williams static ssize_t id_show(struct device *cdev,
434967f7babSDan Williams struct device_attribute *attr,
435967f7babSDan Williams char *buf)
436967f7babSDan Williams {
437967f7babSDan Williams struct enclosure_device *edev = to_enclosure_device(cdev);
438967f7babSDan Williams
439967f7babSDan Williams if (edev->cb->show_id)
440967f7babSDan Williams return edev->cb->show_id(edev, buf);
441967f7babSDan Williams return -EINVAL;
442967f7babSDan Williams }
443967f7babSDan Williams static DEVICE_ATTR_RO(id);
444967f7babSDan Williams
445899826f1SGreg Kroah-Hartman static struct attribute *enclosure_class_attrs[] = {
446899826f1SGreg Kroah-Hartman &dev_attr_components.attr,
447967f7babSDan Williams &dev_attr_id.attr,
448899826f1SGreg Kroah-Hartman NULL,
449d569d5bbSJames Bottomley };
450899826f1SGreg Kroah-Hartman ATTRIBUTE_GROUPS(enclosure_class);
451d569d5bbSJames Bottomley
452d569d5bbSJames Bottomley static struct class enclosure_class = {
453d569d5bbSJames Bottomley .name = "enclosure",
454ee959b00STony Jones .dev_release = enclosure_release,
455899826f1SGreg Kroah-Hartman .dev_groups = enclosure_class_groups,
456d569d5bbSJames Bottomley };
457d569d5bbSJames Bottomley
458d569d5bbSJames Bottomley static const char *const enclosure_status[] = {
459d569d5bbSJames Bottomley [ENCLOSURE_STATUS_UNSUPPORTED] = "unsupported",
460d569d5bbSJames Bottomley [ENCLOSURE_STATUS_OK] = "OK",
461d569d5bbSJames Bottomley [ENCLOSURE_STATUS_CRITICAL] = "critical",
462d569d5bbSJames Bottomley [ENCLOSURE_STATUS_NON_CRITICAL] = "non-critical",
463d569d5bbSJames Bottomley [ENCLOSURE_STATUS_UNRECOVERABLE] = "unrecoverable",
464d569d5bbSJames Bottomley [ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed",
465d569d5bbSJames Bottomley [ENCLOSURE_STATUS_UNKNOWN] = "unknown",
466d569d5bbSJames Bottomley [ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable",
467cc9b2e9fSJames Bottomley [ENCLOSURE_STATUS_MAX] = NULL,
468d569d5bbSJames Bottomley };
469d569d5bbSJames Bottomley
470d569d5bbSJames Bottomley static const char *const enclosure_type[] = {
471d569d5bbSJames Bottomley [ENCLOSURE_COMPONENT_DEVICE] = "device",
472d569d5bbSJames Bottomley [ENCLOSURE_COMPONENT_ARRAY_DEVICE] = "array device",
473d569d5bbSJames Bottomley };
474d569d5bbSJames Bottomley
get_component_fault(struct device * cdev,struct device_attribute * attr,char * buf)475ee959b00STony Jones static ssize_t get_component_fault(struct device *cdev,
476ee959b00STony Jones struct device_attribute *attr, char *buf)
477d569d5bbSJames Bottomley {
478d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
479d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
480d569d5bbSJames Bottomley
481d569d5bbSJames Bottomley if (edev->cb->get_fault)
482d569d5bbSJames Bottomley edev->cb->get_fault(edev, ecomp);
483714f1af1SYe Guojin return sysfs_emit(buf, "%d\n", ecomp->fault);
484d569d5bbSJames Bottomley }
485d569d5bbSJames Bottomley
set_component_fault(struct device * cdev,struct device_attribute * attr,const char * buf,size_t count)486ee959b00STony Jones static ssize_t set_component_fault(struct device *cdev,
487ee959b00STony Jones struct device_attribute *attr,
488ee959b00STony Jones const char *buf, size_t count)
489d569d5bbSJames Bottomley {
490d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
491d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
492d569d5bbSJames Bottomley int val = simple_strtoul(buf, NULL, 0);
493d569d5bbSJames Bottomley
494d569d5bbSJames Bottomley if (edev->cb->set_fault)
495d569d5bbSJames Bottomley edev->cb->set_fault(edev, ecomp, val);
496d569d5bbSJames Bottomley return count;
497d569d5bbSJames Bottomley }
498d569d5bbSJames Bottomley
get_component_status(struct device * cdev,struct device_attribute * attr,char * buf)499ee959b00STony Jones static ssize_t get_component_status(struct device *cdev,
500ee959b00STony Jones struct device_attribute *attr,char *buf)
501d569d5bbSJames Bottomley {
502d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
503d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
504d569d5bbSJames Bottomley
505d569d5bbSJames Bottomley if (edev->cb->get_status)
506d569d5bbSJames Bottomley edev->cb->get_status(edev, ecomp);
507714f1af1SYe Guojin return sysfs_emit(buf, "%s\n", enclosure_status[ecomp->status]);
508d569d5bbSJames Bottomley }
509d569d5bbSJames Bottomley
set_component_status(struct device * cdev,struct device_attribute * attr,const char * buf,size_t count)510ee959b00STony Jones static ssize_t set_component_status(struct device *cdev,
511ee959b00STony Jones struct device_attribute *attr,
512ee959b00STony Jones const char *buf, size_t count)
513d569d5bbSJames Bottomley {
514d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
515d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
516d569d5bbSJames Bottomley int i;
517d569d5bbSJames Bottomley
518d569d5bbSJames Bottomley for (i = 0; enclosure_status[i]; i++) {
519d569d5bbSJames Bottomley if (strncmp(buf, enclosure_status[i],
520d569d5bbSJames Bottomley strlen(enclosure_status[i])) == 0 &&
521d569d5bbSJames Bottomley (buf[strlen(enclosure_status[i])] == '\n' ||
522d569d5bbSJames Bottomley buf[strlen(enclosure_status[i])] == '\0'))
523d569d5bbSJames Bottomley break;
524d569d5bbSJames Bottomley }
525d569d5bbSJames Bottomley
526d569d5bbSJames Bottomley if (enclosure_status[i] && edev->cb->set_status) {
527d569d5bbSJames Bottomley edev->cb->set_status(edev, ecomp, i);
528d569d5bbSJames Bottomley return count;
529d569d5bbSJames Bottomley } else
530d569d5bbSJames Bottomley return -EINVAL;
531d569d5bbSJames Bottomley }
532d569d5bbSJames Bottomley
get_component_active(struct device * cdev,struct device_attribute * attr,char * buf)533ee959b00STony Jones static ssize_t get_component_active(struct device *cdev,
534ee959b00STony Jones struct device_attribute *attr, char *buf)
535d569d5bbSJames Bottomley {
536d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
537d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
538d569d5bbSJames Bottomley
539d569d5bbSJames Bottomley if (edev->cb->get_active)
540d569d5bbSJames Bottomley edev->cb->get_active(edev, ecomp);
541714f1af1SYe Guojin return sysfs_emit(buf, "%d\n", ecomp->active);
542d569d5bbSJames Bottomley }
543d569d5bbSJames Bottomley
set_component_active(struct device * cdev,struct device_attribute * attr,const char * buf,size_t count)544ee959b00STony Jones static ssize_t set_component_active(struct device *cdev,
545ee959b00STony Jones struct device_attribute *attr,
546ee959b00STony Jones const char *buf, size_t count)
547d569d5bbSJames Bottomley {
548d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
549d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
550d569d5bbSJames Bottomley int val = simple_strtoul(buf, NULL, 0);
551d569d5bbSJames Bottomley
552d569d5bbSJames Bottomley if (edev->cb->set_active)
553d569d5bbSJames Bottomley edev->cb->set_active(edev, ecomp, val);
554d569d5bbSJames Bottomley return count;
555d569d5bbSJames Bottomley }
556d569d5bbSJames Bottomley
get_component_locate(struct device * cdev,struct device_attribute * attr,char * buf)557ee959b00STony Jones static ssize_t get_component_locate(struct device *cdev,
558ee959b00STony Jones struct device_attribute *attr, char *buf)
559d569d5bbSJames Bottomley {
560d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
561d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
562d569d5bbSJames Bottomley
563d569d5bbSJames Bottomley if (edev->cb->get_locate)
564d569d5bbSJames Bottomley edev->cb->get_locate(edev, ecomp);
565714f1af1SYe Guojin return sysfs_emit(buf, "%d\n", ecomp->locate);
566d569d5bbSJames Bottomley }
567d569d5bbSJames Bottomley
set_component_locate(struct device * cdev,struct device_attribute * attr,const char * buf,size_t count)568ee959b00STony Jones static ssize_t set_component_locate(struct device *cdev,
569ee959b00STony Jones struct device_attribute *attr,
570ee959b00STony Jones const char *buf, size_t count)
571d569d5bbSJames Bottomley {
572d569d5bbSJames Bottomley struct enclosure_device *edev = to_enclosure_device(cdev->parent);
573d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
574d569d5bbSJames Bottomley int val = simple_strtoul(buf, NULL, 0);
575d569d5bbSJames Bottomley
576d569d5bbSJames Bottomley if (edev->cb->set_locate)
577d569d5bbSJames Bottomley edev->cb->set_locate(edev, ecomp, val);
578d569d5bbSJames Bottomley return count;
579d569d5bbSJames Bottomley }
580d569d5bbSJames Bottomley
get_component_power_status(struct device * cdev,struct device_attribute * attr,char * buf)58108024885SSong Liu static ssize_t get_component_power_status(struct device *cdev,
58208024885SSong Liu struct device_attribute *attr,
58308024885SSong Liu char *buf)
58408024885SSong Liu {
58508024885SSong Liu struct enclosure_device *edev = to_enclosure_device(cdev->parent);
58608024885SSong Liu struct enclosure_component *ecomp = to_enclosure_component(cdev);
58708024885SSong Liu
58808024885SSong Liu if (edev->cb->get_power_status)
58908024885SSong Liu edev->cb->get_power_status(edev, ecomp);
59075106523SMauricio Faria de Oliveira
59175106523SMauricio Faria de Oliveira /* If still uninitialized, the callback failed or does not exist. */
59275106523SMauricio Faria de Oliveira if (ecomp->power_status == -1)
59375106523SMauricio Faria de Oliveira return (edev->cb->get_power_status) ? -EIO : -ENOTTY;
59475106523SMauricio Faria de Oliveira
595714f1af1SYe Guojin return sysfs_emit(buf, "%s\n", ecomp->power_status ? "on" : "off");
59608024885SSong Liu }
59708024885SSong Liu
set_component_power_status(struct device * cdev,struct device_attribute * attr,const char * buf,size_t count)59808024885SSong Liu static ssize_t set_component_power_status(struct device *cdev,
59908024885SSong Liu struct device_attribute *attr,
60008024885SSong Liu const char *buf, size_t count)
60108024885SSong Liu {
60208024885SSong Liu struct enclosure_device *edev = to_enclosure_device(cdev->parent);
60308024885SSong Liu struct enclosure_component *ecomp = to_enclosure_component(cdev);
60408024885SSong Liu int val;
60508024885SSong Liu
60608024885SSong Liu if (strncmp(buf, "on", 2) == 0 &&
60708024885SSong Liu (buf[2] == '\n' || buf[2] == '\0'))
60808024885SSong Liu val = 1;
60908024885SSong Liu else if (strncmp(buf, "off", 3) == 0 &&
61008024885SSong Liu (buf[3] == '\n' || buf[3] == '\0'))
61108024885SSong Liu val = 0;
61208024885SSong Liu else
61308024885SSong Liu return -EINVAL;
61408024885SSong Liu
61508024885SSong Liu if (edev->cb->set_power_status)
61608024885SSong Liu edev->cb->set_power_status(edev, ecomp, val);
61708024885SSong Liu return count;
61808024885SSong Liu }
61908024885SSong Liu
get_component_type(struct device * cdev,struct device_attribute * attr,char * buf)620ee959b00STony Jones static ssize_t get_component_type(struct device *cdev,
621ee959b00STony Jones struct device_attribute *attr, char *buf)
622d569d5bbSJames Bottomley {
623d569d5bbSJames Bottomley struct enclosure_component *ecomp = to_enclosure_component(cdev);
624d569d5bbSJames Bottomley
625714f1af1SYe Guojin return sysfs_emit(buf, "%s\n", enclosure_type[ecomp->type]);
626d569d5bbSJames Bottomley }
627d569d5bbSJames Bottomley
get_component_slot(struct device * cdev,struct device_attribute * attr,char * buf)628921ce7f5SDan Williams static ssize_t get_component_slot(struct device *cdev,
629921ce7f5SDan Williams struct device_attribute *attr, char *buf)
630921ce7f5SDan Williams {
631921ce7f5SDan Williams struct enclosure_component *ecomp = to_enclosure_component(cdev);
632921ce7f5SDan Williams int slot;
633921ce7f5SDan Williams
634921ce7f5SDan Williams /* if the enclosure does not override then use 'number' as a stand-in */
635921ce7f5SDan Williams if (ecomp->slot >= 0)
636921ce7f5SDan Williams slot = ecomp->slot;
637921ce7f5SDan Williams else
638921ce7f5SDan Williams slot = ecomp->number;
639921ce7f5SDan Williams
640714f1af1SYe Guojin return sysfs_emit(buf, "%d\n", slot);
641921ce7f5SDan Williams }
642d569d5bbSJames Bottomley
643cb6b7f40SJames Bottomley static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault,
644cb6b7f40SJames Bottomley set_component_fault);
645cb6b7f40SJames Bottomley static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, get_component_status,
646cb6b7f40SJames Bottomley set_component_status);
647cb6b7f40SJames Bottomley static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active,
648cb6b7f40SJames Bottomley set_component_active);
649cb6b7f40SJames Bottomley static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate,
650cb6b7f40SJames Bottomley set_component_locate);
65108024885SSong Liu static DEVICE_ATTR(power_status, S_IRUGO | S_IWUSR, get_component_power_status,
65208024885SSong Liu set_component_power_status);
653cb6b7f40SJames Bottomley static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL);
654921ce7f5SDan Williams static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, NULL);
655cb6b7f40SJames Bottomley
656cb6b7f40SJames Bottomley static struct attribute *enclosure_component_attrs[] = {
657cb6b7f40SJames Bottomley &dev_attr_fault.attr,
658cb6b7f40SJames Bottomley &dev_attr_status.attr,
659cb6b7f40SJames Bottomley &dev_attr_active.attr,
660cb6b7f40SJames Bottomley &dev_attr_locate.attr,
66108024885SSong Liu &dev_attr_power_status.attr,
662cb6b7f40SJames Bottomley &dev_attr_type.attr,
663921ce7f5SDan Williams &dev_attr_slot.attr,
664cb6b7f40SJames Bottomley NULL
665d569d5bbSJames Bottomley };
666899826f1SGreg Kroah-Hartman ATTRIBUTE_GROUPS(enclosure_component);
667d569d5bbSJames Bottomley
enclosure_init(void)668d569d5bbSJames Bottomley static int __init enclosure_init(void)
669d569d5bbSJames Bottomley {
670750b54deSArvind Yadav return class_register(&enclosure_class);
671d569d5bbSJames Bottomley }
672d569d5bbSJames Bottomley
enclosure_exit(void)673d569d5bbSJames Bottomley static void __exit enclosure_exit(void)
674d569d5bbSJames Bottomley {
675d569d5bbSJames Bottomley class_unregister(&enclosure_class);
676d569d5bbSJames Bottomley }
677d569d5bbSJames Bottomley
678d569d5bbSJames Bottomley module_init(enclosure_init);
679d569d5bbSJames Bottomley module_exit(enclosure_exit);
680d569d5bbSJames Bottomley
681d569d5bbSJames Bottomley MODULE_AUTHOR("James Bottomley");
682d569d5bbSJames Bottomley MODULE_DESCRIPTION("Enclosure Services");
683d569d5bbSJames Bottomley MODULE_LICENSE("GPL v2");
684