Merge branch 'v28-range-hrtimers-for-linus-v2' of git://git.kernel.org/pub/scm/linux...
[linux-2.6] / drivers / uwb / wlp / sysfs.c
1 /*
2  * WiMedia Logical Link Control Protocol (WLP)
3  * sysfs functions
4  *
5  * Copyright (C) 2007 Intel Corporation
6  * Reinette Chatre <reinette.chatre@intel.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version
10  * 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301, USA.
21  *
22  *
23  * FIXME: Docs
24  *
25  */
26
27 #include <linux/wlp.h>
28 #include "wlp-internal.h"
29
30 static
31 size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize,
32                              struct wlp_wssid_e *wssid_e)
33 {
34         size_t used = 0;
35         used += scnprintf(buf, bufsize, " WSS: ");
36         used += wlp_wss_uuid_print(buf + used, bufsize - used,
37                                    &wssid_e->wssid);
38
39         if (wssid_e->info != NULL) {
40                 used += scnprintf(buf + used, bufsize - used, " ");
41                 used += uwb_mac_addr_print(buf + used, bufsize - used,
42                                            &wssid_e->info->bcast);
43                 used += scnprintf(buf + used, bufsize - used, " %u %u %s\n",
44                                   wssid_e->info->accept_enroll,
45                                   wssid_e->info->sec_status,
46                                   wssid_e->info->name);
47         }
48         return used;
49 }
50
51 /**
52  * Print out information learned from neighbor discovery
53  *
54  * Some fields being printed may not be included in the device discovery
55  * information (it is not mandatory). We are thus careful how the
56  * information is printed to ensure it is clear to the user what field is
57  * being referenced.
58  * The information being printed is for one time use - temporary storage is
59  * cleaned after it is printed.
60  *
61  * Ideally sysfs output should be on one line. The information printed here
62  * contain a few strings so it will be hard to parse if they are all
63  * printed on the same line - without agreeing on a standard field
64  * separator.
65  */
66 static
67 ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf,
68                                    size_t bufsize)
69 {
70         size_t used = 0;
71         struct wlp_neighbor_e *neighb;
72         struct wlp_wssid_e *wssid_e;
73
74         mutex_lock(&wlp->nbmutex);
75         used = scnprintf(buf, bufsize, "#Neighbor information\n"
76                          "#uuid dev_addr\n"
77                          "# Device Name:\n# Model Name:\n# Manufacturer:\n"
78                          "# Model Nr:\n# Serial:\n"
79                          "# Pri Dev type: CategoryID OUI OUISubdiv "
80                          "SubcategoryID\n"
81                          "# WSS: WSSID WSS_name accept_enroll sec_status "
82                          "bcast\n"
83                          "# WSS: WSSID WSS_name accept_enroll sec_status "
84                          "bcast\n\n");
85         list_for_each_entry(neighb, &wlp->neighbors, node) {
86                 if (bufsize - used <= 0)
87                         goto out;
88                 used += wlp_wss_uuid_print(buf + used, bufsize - used,
89                                            &neighb->uuid);
90                 buf[used++] = ' ';
91                 used += uwb_dev_addr_print(buf + used, bufsize - used,
92                                            &neighb->uwb_dev->dev_addr);
93                 if (neighb->info != NULL)
94                         used += scnprintf(buf + used, bufsize - used,
95                                           "\n Device Name: %s\n"
96                                           " Model Name: %s\n"
97                                           " Manufacturer:%s \n"
98                                           " Model Nr: %s\n"
99                                           " Serial: %s\n"
100                                           " Pri Dev type: "
101                                           "%u %02x:%02x:%02x %u %u\n",
102                                           neighb->info->name,
103                                           neighb->info->model_name,
104                                           neighb->info->manufacturer,
105                                           neighb->info->model_nr,
106                                           neighb->info->serial,
107                                           neighb->info->prim_dev_type.category,
108                                           neighb->info->prim_dev_type.OUI[0],
109                                           neighb->info->prim_dev_type.OUI[1],
110                                           neighb->info->prim_dev_type.OUI[2],
111                                           neighb->info->prim_dev_type.OUIsubdiv,
112                                           neighb->info->prim_dev_type.subID);
113                 list_for_each_entry(wssid_e, &neighb->wssid, node) {
114                         used += wlp_wss_wssid_e_print(buf + used,
115                                                       bufsize - used,
116                                                       wssid_e);
117                 }
118                 buf[used++] = '\n';
119                 wlp_remove_neighbor_tmp_info(neighb);
120         }
121
122
123 out:
124         mutex_unlock(&wlp->nbmutex);
125         return used;
126 }
127
128
129 /**
130  * Show properties of all WSS in neighborhood.
131  *
132  * Will trigger a complete discovery of WSS activated by this device and
133  * its neighbors.
134  */
135 ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf)
136 {
137         wlp_discover(wlp);
138         return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE);
139 }
140 EXPORT_SYMBOL_GPL(wlp_neighborhood_show);
141
142 static
143 ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf,
144                                   size_t bufsize)
145 {
146         ssize_t result;
147
148         result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid);
149         result += scnprintf(buf + result, bufsize - result, " ");
150         result += uwb_mac_addr_print(buf + result, bufsize - result,
151                                      &wss->bcast);
152         result += scnprintf(buf + result, bufsize - result,
153                             " 0x%02x %u ", wss->hash, wss->secure_status);
154         result += wlp_wss_key_print(buf + result, bufsize - result,
155                                     wss->master_key);
156         result += scnprintf(buf + result, bufsize - result, " 0x%02x ",
157                             wss->tag);
158         result += uwb_mac_addr_print(buf + result, bufsize - result,
159                                      &wss->virtual_addr);
160         result += scnprintf(buf + result, bufsize - result, " %s", wss->name);
161         result += scnprintf(buf + result, bufsize - result,
162                             "\n\n#WSSID\n#WSS broadcast address\n"
163                             "#WSS hash\n#WSS secure status\n"
164                             "#WSS master key\n#WSS local tag\n"
165                             "#WSS local virtual EUI-48\n#WSS name\n");
166         return result;
167 }
168
169 /**
170  * Show which WSS is activated.
171  */
172 ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf)
173 {
174         int result = 0;
175
176         if (mutex_lock_interruptible(&wss->mutex))
177                 goto out;
178         if (wss->state >= WLP_WSS_STATE_ACTIVE)
179                 result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
180         else
181                 result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n");
182         result += scnprintf(buf + result, PAGE_SIZE - result,
183                         "\n\n"
184                         "# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT "
185                         "NAME #create new WSS\n"
186                         "# echo WSSID [DEV ADDR] #enroll in and activate "
187                         "existing WSS, can request registrar\n"
188                         "#\n"
189                         "# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n"
190                         "# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n"
191                         "# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n"
192                         "# NAME is the text string identifying the WSS\n"
193                         "# DEV ADDR is the device address of neighbor "
194                         "that should be registrar. Eg. 32:AB\n");
195
196         mutex_unlock(&wss->mutex);
197 out:
198         return result;
199
200 }
201 EXPORT_SYMBOL_GPL(wlp_wss_activate_show);
202
203 /**
204  * Create/activate a new WSS or enroll/activate in neighboring WSS
205  *
206  * The user can provide the WSSID of a WSS in which it wants to enroll.
207  * Only the WSSID is necessary if the WSS have been discovered before. If
208  * the WSS has not been discovered before, or the user wants to use a
209  * particular neighbor as its registrar, then the user can also provide a
210  * device address or the neighbor that will be used as registrar.
211  *
212  * A new WSS is created when the user provides a WSSID, secure status, and
213  * WSS name.
214  */
215 ssize_t wlp_wss_activate_store(struct wlp_wss *wss,
216                                const char *buf, size_t size)
217 {
218         ssize_t result = -EINVAL;
219         struct wlp_uuid wssid;
220         struct uwb_dev_addr dev;
221         struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
222         char name[65];
223         unsigned sec_status, accept;
224         memset(name, 0, sizeof(name));
225         result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
226                         "%02hhx %02hhx %02hhx %02hhx "
227                         "%02hhx %02hhx %02hhx %02hhx "
228                         "%02hhx %02hhx %02hhx %02hhx "
229                         "%02hhx:%02hhx",
230                         &wssid.data[0] , &wssid.data[1],
231                         &wssid.data[2] , &wssid.data[3],
232                         &wssid.data[4] , &wssid.data[5],
233                         &wssid.data[6] , &wssid.data[7],
234                         &wssid.data[8] , &wssid.data[9],
235                         &wssid.data[10], &wssid.data[11],
236                         &wssid.data[12], &wssid.data[13],
237                         &wssid.data[14], &wssid.data[15],
238                         &dev.data[1], &dev.data[0]);
239         if (result == 16 || result == 17) {
240                 result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
241                                 "%02hhx %02hhx %02hhx %02hhx "
242                                 "%02hhx %02hhx %02hhx %02hhx "
243                                 "%02hhx %02hhx %02hhx %02hhx "
244                                 "%u %u %64c",
245                                 &wssid.data[0] , &wssid.data[1],
246                                 &wssid.data[2] , &wssid.data[3],
247                                 &wssid.data[4] , &wssid.data[5],
248                                 &wssid.data[6] , &wssid.data[7],
249                                 &wssid.data[8] , &wssid.data[9],
250                                 &wssid.data[10], &wssid.data[11],
251                                 &wssid.data[12], &wssid.data[13],
252                                 &wssid.data[14], &wssid.data[15],
253                                 &sec_status, &accept, name);
254                 if (result == 16)
255                         result = wlp_wss_enroll_activate(wss, &wssid, &bcast);
256                 else if (result == 19) {
257                         sec_status = sec_status == 0 ? 0 : 1;
258                         accept = accept == 0 ? 0 : 1;
259                         /* We read name using %c, so the newline needs to be
260                          * removed */
261                         if (strlen(name) != sizeof(name) - 1)
262                                 name[strlen(name) - 1] = '\0';
263                         result = wlp_wss_create_activate(wss, &wssid, name,
264                                                          sec_status, accept);
265                 } else
266                         result = -EINVAL;
267         } else if (result == 18)
268                 result = wlp_wss_enroll_activate(wss, &wssid, &dev);
269         else
270                 result = -EINVAL;
271         return result < 0 ? result : size;
272 }
273 EXPORT_SYMBOL_GPL(wlp_wss_activate_store);
274
275 /**
276  * Show the UUID of this host
277  */
278 ssize_t wlp_uuid_show(struct wlp *wlp, char *buf)
279 {
280         ssize_t result = 0;
281
282         mutex_lock(&wlp->mutex);
283         result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid);
284         buf[result++] = '\n';
285         mutex_unlock(&wlp->mutex);
286         return result;
287 }
288 EXPORT_SYMBOL_GPL(wlp_uuid_show);
289
290 /**
291  * Store a new UUID for this host
292  *
293  * According to the spec this should be encoded as an octet string in the
294  * order the octets are shown in string representation in RFC 4122 (WLP
295  * 0.99 [Table 6])
296  *
297  * We do not check value provided by user.
298  */
299 ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size)
300 {
301         ssize_t result;
302         struct wlp_uuid uuid;
303
304         mutex_lock(&wlp->mutex);
305         result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
306                         "%02hhx %02hhx %02hhx %02hhx "
307                         "%02hhx %02hhx %02hhx %02hhx "
308                         "%02hhx %02hhx %02hhx %02hhx ",
309                         &uuid.data[0] , &uuid.data[1],
310                         &uuid.data[2] , &uuid.data[3],
311                         &uuid.data[4] , &uuid.data[5],
312                         &uuid.data[6] , &uuid.data[7],
313                         &uuid.data[8] , &uuid.data[9],
314                         &uuid.data[10], &uuid.data[11],
315                         &uuid.data[12], &uuid.data[13],
316                         &uuid.data[14], &uuid.data[15]);
317         if (result != 16) {
318                 result = -EINVAL;
319                 goto error;
320         }
321         wlp->uuid = uuid;
322 error:
323         mutex_unlock(&wlp->mutex);
324         return result < 0 ? result : size;
325 }
326 EXPORT_SYMBOL_GPL(wlp_uuid_store);
327
328 /**
329  * Show contents of members of device information structure
330  */
331 #define wlp_dev_info_show(type)                                         \
332 ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf)               \
333 {                                                                       \
334         ssize_t result = 0;                                             \
335         mutex_lock(&wlp->mutex);                                        \
336         if (wlp->dev_info == NULL) {                                    \
337                 result = __wlp_setup_device_info(wlp);                  \
338                 if (result < 0)                                         \
339                         goto out;                                       \
340         }                                                               \
341         result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\
342 out:                                                                    \
343         mutex_unlock(&wlp->mutex);                                      \
344         return result;                                                  \
345 }                                                                       \
346 EXPORT_SYMBOL_GPL(wlp_dev_##type##_show);
347
348 wlp_dev_info_show(name)
349 wlp_dev_info_show(model_name)
350 wlp_dev_info_show(model_nr)
351 wlp_dev_info_show(manufacturer)
352 wlp_dev_info_show(serial)
353
354 /**
355  * Store contents of members of device information structure
356  */
357 #define wlp_dev_info_store(type, len)                                   \
358 ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\
359 {                                                                       \
360         ssize_t result;                                                 \
361         char format[10];                                                \
362         mutex_lock(&wlp->mutex);                                        \
363         if (wlp->dev_info == NULL) {                                    \
364                 result = __wlp_alloc_device_info(wlp);                  \
365                 if (result < 0)                                         \
366                         goto out;                                       \
367         }                                                               \
368         memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type));    \
369         sprintf(format, "%%%uc", len);                                  \
370         result = sscanf(buf, format, wlp->dev_info->type);              \
371 out:                                                                    \
372         mutex_unlock(&wlp->mutex);                                      \
373         return result < 0 ? result : size;                              \
374 }                                                                       \
375 EXPORT_SYMBOL_GPL(wlp_dev_##type##_store);
376
377 wlp_dev_info_store(name, 32)
378 wlp_dev_info_store(manufacturer, 64)
379 wlp_dev_info_store(model_name, 32)
380 wlp_dev_info_store(model_nr, 32)
381 wlp_dev_info_store(serial, 32)
382
383 static
384 const char *__wlp_dev_category[] = {
385         [WLP_DEV_CAT_COMPUTER] = "Computer",
386         [WLP_DEV_CAT_INPUT] = "Input device",
387         [WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or "
388                                               "Copier",
389         [WLP_DEV_CAT_CAMERA] = "Camera",
390         [WLP_DEV_CAT_STORAGE] = "Storage Network",
391         [WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure",
392         [WLP_DEV_CAT_DISPLAY] = "Display",
393         [WLP_DEV_CAT_MULTIM] = "Multimedia device",
394         [WLP_DEV_CAT_GAMING] = "Gaming device",
395         [WLP_DEV_CAT_TELEPHONE] = "Telephone",
396         [WLP_DEV_CAT_OTHER] = "Other",
397 };
398
399 static
400 const char *wlp_dev_category_str(unsigned cat)
401 {
402         if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
403             || cat == WLP_DEV_CAT_OTHER)
404                 return __wlp_dev_category[cat];
405         return "unknown category";
406 }
407
408 ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf)
409 {
410         ssize_t result = 0;
411         mutex_lock(&wlp->mutex);
412         if (wlp->dev_info == NULL) {
413                 result = __wlp_setup_device_info(wlp);
414                 if (result < 0)
415                         goto out;
416         }
417         result = scnprintf(buf, PAGE_SIZE, "%s\n",
418                   wlp_dev_category_str(wlp->dev_info->prim_dev_type.category));
419 out:
420         mutex_unlock(&wlp->mutex);
421         return result;
422 }
423 EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show);
424
425 ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf,
426                                     size_t size)
427 {
428         ssize_t result;
429         u16 cat;
430         mutex_lock(&wlp->mutex);
431         if (wlp->dev_info == NULL) {
432                 result = __wlp_alloc_device_info(wlp);
433                 if (result < 0)
434                         goto out;
435         }
436         result = sscanf(buf, "%hu", &cat);
437         if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
438             || cat == WLP_DEV_CAT_OTHER)
439                 wlp->dev_info->prim_dev_type.category = cat;
440         else
441                 result = -EINVAL;
442 out:
443         mutex_unlock(&wlp->mutex);
444         return result < 0 ? result : size;
445 }
446 EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store);
447
448 ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf)
449 {
450         ssize_t result = 0;
451         mutex_lock(&wlp->mutex);
452         if (wlp->dev_info == NULL) {
453                 result = __wlp_setup_device_info(wlp);
454                 if (result < 0)
455                         goto out;
456         }
457         result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n",
458                            wlp->dev_info->prim_dev_type.OUI[0],
459                            wlp->dev_info->prim_dev_type.OUI[1],
460                            wlp->dev_info->prim_dev_type.OUI[2]);
461 out:
462         mutex_unlock(&wlp->mutex);
463         return result;
464 }
465 EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show);
466
467 ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size)
468 {
469         ssize_t result;
470         u8 OUI[3];
471         mutex_lock(&wlp->mutex);
472         if (wlp->dev_info == NULL) {
473                 result = __wlp_alloc_device_info(wlp);
474                 if (result < 0)
475                         goto out;
476         }
477         result = sscanf(buf, "%hhx:%hhx:%hhx",
478                         &OUI[0], &OUI[1], &OUI[2]);
479         if (result != 3) {
480                 result = -EINVAL;
481                 goto out;
482         } else
483                 memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI));
484 out:
485         mutex_unlock(&wlp->mutex);
486         return result < 0 ? result : size;
487 }
488 EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store);
489
490
491 ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf)
492 {
493         ssize_t result = 0;
494         mutex_lock(&wlp->mutex);
495         if (wlp->dev_info == NULL) {
496                 result = __wlp_setup_device_info(wlp);
497                 if (result < 0)
498                         goto out;
499         }
500         result = scnprintf(buf, PAGE_SIZE, "%u\n",
501                            wlp->dev_info->prim_dev_type.OUIsubdiv);
502 out:
503         mutex_unlock(&wlp->mutex);
504         return result;
505 }
506 EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show);
507
508 ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf,
509                                    size_t size)
510 {
511         ssize_t result;
512         unsigned sub;
513         u8 max_sub = ~0;
514         mutex_lock(&wlp->mutex);
515         if (wlp->dev_info == NULL) {
516                 result = __wlp_alloc_device_info(wlp);
517                 if (result < 0)
518                         goto out;
519         }
520         result = sscanf(buf, "%u", &sub);
521         if (sub <= max_sub)
522                 wlp->dev_info->prim_dev_type.OUIsubdiv = sub;
523         else
524                 result = -EINVAL;
525 out:
526         mutex_unlock(&wlp->mutex);
527         return result < 0 ? result : size;
528 }
529 EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store);
530
531 ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf)
532 {
533         ssize_t result = 0;
534         mutex_lock(&wlp->mutex);
535         if (wlp->dev_info == NULL) {
536                 result = __wlp_setup_device_info(wlp);
537                 if (result < 0)
538                         goto out;
539         }
540         result = scnprintf(buf, PAGE_SIZE, "%u\n",
541                            wlp->dev_info->prim_dev_type.subID);
542 out:
543         mutex_unlock(&wlp->mutex);
544         return result;
545 }
546 EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show);
547
548 ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf,
549                                   size_t size)
550 {
551         ssize_t result;
552         unsigned sub;
553         __le16 max_sub = ~0;
554         mutex_lock(&wlp->mutex);
555         if (wlp->dev_info == NULL) {
556                 result = __wlp_alloc_device_info(wlp);
557                 if (result < 0)
558                         goto out;
559         }
560         result = sscanf(buf, "%u", &sub);
561         if (sub <= max_sub)
562                 wlp->dev_info->prim_dev_type.subID = sub;
563         else
564                 result = -EINVAL;
565 out:
566         mutex_unlock(&wlp->mutex);
567         return result < 0 ? result : size;
568 }
569 EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store);
570
571 /**
572  * Subsystem implementation for interaction with individual WSS via sysfs
573  *
574  * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt
575  */
576
577 #define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj)
578 #define attr_to_wlp_wss_attr(_attr) \
579         container_of(_attr, struct wlp_wss_attribute, attr)
580
581 /**
582  * Sysfs subsystem: forward read calls
583  *
584  * Sysfs operation for forwarding read call to the show method of the
585  * attribute owner
586  */
587 static
588 ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr,
589                           char *buf)
590 {
591         struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
592         struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
593         ssize_t ret = -EIO;
594
595         if (wss_attr->show)
596                 ret = wss_attr->show(wss, buf);
597         return ret;
598 }
599 /**
600  * Sysfs subsystem: forward write calls
601  *
602  * Sysfs operation for forwarding write call to the store method of the
603  * attribute owner
604  */
605 static
606 ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr,
607                            const char *buf, size_t count)
608 {
609         struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
610         struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
611         ssize_t ret = -EIO;
612
613         if (wss_attr->store)
614                 ret = wss_attr->store(wss, buf, count);
615         return ret;
616 }
617
618 static
619 struct sysfs_ops wss_sysfs_ops = {
620         .show   = wlp_wss_attr_show,
621         .store  = wlp_wss_attr_store,
622 };
623
624 struct kobj_type wss_ktype = {
625         .release        = wlp_wss_release,
626         .sysfs_ops      = &wss_sysfs_ops,
627 };
628
629
630 /**
631  * Sysfs files for individual WSS
632  */
633
634 /**
635  * Print static properties of this WSS
636  *
637  * The name of a WSS may not be null teminated. It's max size is 64 bytes
638  * so we copy it to a larger array just to make sure we print sane data.
639  */
640 static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf)
641 {
642         int result = 0;
643
644         if (mutex_lock_interruptible(&wss->mutex))
645                 goto out;
646         result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
647         mutex_unlock(&wss->mutex);
648 out:
649         return result;
650 }
651 WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL);
652
653 /**
654  * Print all connected members of this WSS
655  * The EDA cache contains all members of WSS neighborhood.
656  */
657 static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf)
658 {
659         struct wlp *wlp = container_of(wss, struct wlp, wss);
660         return wlp_eda_show(wlp, buf);
661 }
662 WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL);
663
664 static
665 const char *__wlp_strstate[] = {
666         "none",
667         "partially enrolled",
668         "enrolled",
669         "active",
670         "connected",
671 };
672
673 static const char *wlp_wss_strstate(unsigned state)
674 {
675         if (state >= ARRAY_SIZE(__wlp_strstate))
676                 return "unknown state";
677         return __wlp_strstate[state];
678 }
679
680 /*
681  * Print current state of this WSS
682  */
683 static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf)
684 {
685         int result = 0;
686
687         if (mutex_lock_interruptible(&wss->mutex))
688                 goto out;
689         result = scnprintf(buf, PAGE_SIZE, "%s\n",
690                            wlp_wss_strstate(wss->state));
691         mutex_unlock(&wss->mutex);
692 out:
693         return result;
694 }
695 WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL);
696
697
698 static
699 struct attribute *wss_attrs[] = {
700         &wss_attr_properties.attr,
701         &wss_attr_members.attr,
702         &wss_attr_state.attr,
703         NULL,
704 };
705
706 struct attribute_group wss_attr_group = {
707         .name = NULL,   /* we want them in the same directory */
708         .attrs = wss_attrs,
709 };