]>
Commit | Line | Data |
---|---|---|
f5144854 RC |
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 | */ | |
f5144854 | 26 | #include <linux/wlp.h> |
bce83697 | 27 | |
f5144854 RC |
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 | ||
52cf25d0 | 618 | static const struct sysfs_ops wss_sysfs_ops = { |
f5144854 RC |
619 | .show = wlp_wss_attr_show, |
620 | .store = wlp_wss_attr_store, | |
621 | }; | |
622 | ||
623 | struct kobj_type wss_ktype = { | |
624 | .release = wlp_wss_release, | |
625 | .sysfs_ops = &wss_sysfs_ops, | |
626 | }; | |
627 | ||
628 | ||
629 | /** | |
630 | * Sysfs files for individual WSS | |
631 | */ | |
632 | ||
633 | /** | |
634 | * Print static properties of this WSS | |
635 | * | |
636 | * The name of a WSS may not be null teminated. It's max size is 64 bytes | |
637 | * so we copy it to a larger array just to make sure we print sane data. | |
638 | */ | |
639 | static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf) | |
640 | { | |
641 | int result = 0; | |
642 | ||
643 | if (mutex_lock_interruptible(&wss->mutex)) | |
644 | goto out; | |
645 | result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); | |
646 | mutex_unlock(&wss->mutex); | |
647 | out: | |
648 | return result; | |
649 | } | |
650 | WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL); | |
651 | ||
652 | /** | |
653 | * Print all connected members of this WSS | |
654 | * The EDA cache contains all members of WSS neighborhood. | |
655 | */ | |
656 | static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf) | |
657 | { | |
658 | struct wlp *wlp = container_of(wss, struct wlp, wss); | |
659 | return wlp_eda_show(wlp, buf); | |
660 | } | |
661 | WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL); | |
662 | ||
663 | static | |
664 | const char *__wlp_strstate[] = { | |
665 | "none", | |
666 | "partially enrolled", | |
667 | "enrolled", | |
668 | "active", | |
669 | "connected", | |
670 | }; | |
671 | ||
672 | static const char *wlp_wss_strstate(unsigned state) | |
673 | { | |
674 | if (state >= ARRAY_SIZE(__wlp_strstate)) | |
675 | return "unknown state"; | |
676 | return __wlp_strstate[state]; | |
677 | } | |
678 | ||
679 | /* | |
680 | * Print current state of this WSS | |
681 | */ | |
682 | static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf) | |
683 | { | |
684 | int result = 0; | |
685 | ||
686 | if (mutex_lock_interruptible(&wss->mutex)) | |
687 | goto out; | |
688 | result = scnprintf(buf, PAGE_SIZE, "%s\n", | |
689 | wlp_wss_strstate(wss->state)); | |
690 | mutex_unlock(&wss->mutex); | |
691 | out: | |
692 | return result; | |
693 | } | |
694 | WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL); | |
695 | ||
696 | ||
697 | static | |
698 | struct attribute *wss_attrs[] = { | |
699 | &wss_attr_properties.attr, | |
700 | &wss_attr_members.attr, | |
701 | &wss_attr_state.attr, | |
702 | NULL, | |
703 | }; | |
704 | ||
705 | struct attribute_group wss_attr_group = { | |
706 | .name = NULL, /* we want them in the same directory */ | |
707 | .attrs = wss_attrs, | |
708 | }; |