diff options
Diffstat (limited to 'drivers/uwb/wlp/sysfs.c')
-rw-r--r-- | drivers/uwb/wlp/sysfs.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/drivers/uwb/wlp/sysfs.c b/drivers/uwb/wlp/sysfs.c new file mode 100644 index 000000000000..1bb9b1f97d47 --- /dev/null +++ b/drivers/uwb/wlp/sysfs.c | |||
@@ -0,0 +1,709 @@ | |||
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 | }; | ||