• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <linux/wlp.h>
27 
28 #include "wlp-internal.h"
29 
30 static
wlp_wss_wssid_e_print(char * buf,size_t bufsize,struct wlp_wssid_e * wssid_e)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
wlp_wss_neighborhood_print_remove(struct wlp * wlp,char * buf,size_t bufsize)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  */
wlp_neighborhood_show(struct wlp * wlp,char * buf)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
__wlp_wss_properties_show(struct wlp_wss * wss,char * buf,size_t bufsize)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  */
wlp_wss_activate_show(struct wlp_wss * wss,char * buf)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  */
wlp_wss_activate_store(struct wlp_wss * wss,const char * buf,size_t size)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  */
wlp_uuid_show(struct wlp * wlp,char * buf)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  */
wlp_uuid_store(struct wlp * wlp,const char * buf,size_t size)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
wlp_dev_category_str(unsigned cat)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 
wlp_dev_prim_category_show(struct wlp * wlp,char * buf)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 
wlp_dev_prim_category_store(struct wlp * wlp,const char * buf,size_t size)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 
wlp_dev_prim_OUI_show(struct wlp * wlp,char * buf)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 
wlp_dev_prim_OUI_store(struct wlp * wlp,const char * buf,size_t size)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 
wlp_dev_prim_OUI_sub_show(struct wlp * wlp,char * buf)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 
wlp_dev_prim_OUI_sub_store(struct wlp * wlp,const char * buf,size_t size)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 
wlp_dev_prim_subcat_show(struct wlp * wlp,char * buf)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 
wlp_dev_prim_subcat_store(struct wlp * wlp,const char * buf,size_t size)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
wlp_wss_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)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
wlp_wss_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)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  */
wlp_wss_properties_show(struct wlp_wss * wss,char * buf)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  */
wlp_wss_members_show(struct wlp_wss * wss,char * buf)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 
wlp_wss_strstate(unsigned state)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  */
wlp_wss_state_show(struct wlp_wss * wss,char * buf)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 };
710