| 
									
										
										
										
											2008-09-17 16:34:16 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * WiMedia Logical Link Control Protocol (WLP) | 
					
						
							|  |  |  |  * sysfs functions | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2007 Intel Corporation | 
					
						
							|  |  |  |  * Reinette Chatre <reinette.chatre@intel.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License version | 
					
						
							|  |  |  |  * 2 as published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 
					
						
							|  |  |  |  * 02110-1301, USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * FIXME: Docs | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/wlp.h>
 | 
					
						
							| 
									
										
										
										
											2008-12-22 18:22:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-17 16:34:16 +01:00
										 |  |  | #include "wlp-internal.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize, | 
					
						
							|  |  |  | 			     struct wlp_wssid_e *wssid_e) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	size_t used = 0; | 
					
						
							|  |  |  | 	used += scnprintf(buf, bufsize, " WSS: "); | 
					
						
							|  |  |  | 	used += wlp_wss_uuid_print(buf + used, bufsize - used, | 
					
						
							|  |  |  | 				   &wssid_e->wssid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (wssid_e->info != NULL) { | 
					
						
							|  |  |  | 		used += scnprintf(buf + used, bufsize - used, " "); | 
					
						
							|  |  |  | 		used += uwb_mac_addr_print(buf + used, bufsize - used, | 
					
						
							|  |  |  | 					   &wssid_e->info->bcast); | 
					
						
							|  |  |  | 		used += scnprintf(buf + used, bufsize - used, " %u %u %s\n", | 
					
						
							|  |  |  | 				  wssid_e->info->accept_enroll, | 
					
						
							|  |  |  | 				  wssid_e->info->sec_status, | 
					
						
							|  |  |  | 				  wssid_e->info->name); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return used; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Print out information learned from neighbor discovery | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Some fields being printed may not be included in the device discovery | 
					
						
							|  |  |  |  * information (it is not mandatory). We are thus careful how the | 
					
						
							|  |  |  |  * information is printed to ensure it is clear to the user what field is | 
					
						
							|  |  |  |  * being referenced. | 
					
						
							|  |  |  |  * The information being printed is for one time use - temporary storage is | 
					
						
							|  |  |  |  * cleaned after it is printed. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Ideally sysfs output should be on one line. The information printed here | 
					
						
							|  |  |  |  * contain a few strings so it will be hard to parse if they are all | 
					
						
							|  |  |  |  * printed on the same line - without agreeing on a standard field | 
					
						
							|  |  |  |  * separator. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf, | 
					
						
							|  |  |  | 				   size_t bufsize) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	size_t used = 0; | 
					
						
							|  |  |  | 	struct wlp_neighbor_e *neighb; | 
					
						
							|  |  |  | 	struct wlp_wssid_e *wssid_e; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&wlp->nbmutex); | 
					
						
							|  |  |  | 	used = scnprintf(buf, bufsize, "#Neighbor information\n" | 
					
						
							|  |  |  | 			 "#uuid dev_addr\n" | 
					
						
							|  |  |  | 			 "# Device Name:\n# Model Name:\n# Manufacturer:\n" | 
					
						
							|  |  |  | 			 "# Model Nr:\n# Serial:\n" | 
					
						
							|  |  |  | 			 "# Pri Dev type: CategoryID OUI OUISubdiv " | 
					
						
							|  |  |  | 			 "SubcategoryID\n" | 
					
						
							|  |  |  | 			 "# WSS: WSSID WSS_name accept_enroll sec_status " | 
					
						
							|  |  |  | 			 "bcast\n" | 
					
						
							|  |  |  | 			 "# WSS: WSSID WSS_name accept_enroll sec_status " | 
					
						
							|  |  |  | 			 "bcast\n\n"); | 
					
						
							|  |  |  | 	list_for_each_entry(neighb, &wlp->neighbors, node) { | 
					
						
							|  |  |  | 		if (bufsize - used <= 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		used += wlp_wss_uuid_print(buf + used, bufsize - used, | 
					
						
							|  |  |  | 					   &neighb->uuid); | 
					
						
							|  |  |  | 		buf[used++] = ' '; | 
					
						
							|  |  |  | 		used += uwb_dev_addr_print(buf + used, bufsize - used, | 
					
						
							|  |  |  | 					   &neighb->uwb_dev->dev_addr); | 
					
						
							|  |  |  | 		if (neighb->info != NULL) | 
					
						
							|  |  |  | 			used += scnprintf(buf + used, bufsize - used, | 
					
						
							|  |  |  | 					  "\n Device Name: %s\n" | 
					
						
							|  |  |  | 					  " Model Name: %s\n" | 
					
						
							|  |  |  | 					  " Manufacturer:%s \n" | 
					
						
							|  |  |  | 					  " Model Nr: %s\n" | 
					
						
							|  |  |  | 					  " Serial: %s\n" | 
					
						
							|  |  |  | 					  " Pri Dev type: " | 
					
						
							|  |  |  | 					  "%u %02x:%02x:%02x %u %u\n", | 
					
						
							|  |  |  | 					  neighb->info->name, | 
					
						
							|  |  |  | 					  neighb->info->model_name, | 
					
						
							|  |  |  | 					  neighb->info->manufacturer, | 
					
						
							|  |  |  | 					  neighb->info->model_nr, | 
					
						
							|  |  |  | 					  neighb->info->serial, | 
					
						
							|  |  |  | 					  neighb->info->prim_dev_type.category, | 
					
						
							|  |  |  | 					  neighb->info->prim_dev_type.OUI[0], | 
					
						
							|  |  |  | 					  neighb->info->prim_dev_type.OUI[1], | 
					
						
							|  |  |  | 					  neighb->info->prim_dev_type.OUI[2], | 
					
						
							|  |  |  | 					  neighb->info->prim_dev_type.OUIsubdiv, | 
					
						
							|  |  |  | 					  neighb->info->prim_dev_type.subID); | 
					
						
							|  |  |  | 		list_for_each_entry(wssid_e, &neighb->wssid, node) { | 
					
						
							|  |  |  | 			used += wlp_wss_wssid_e_print(buf + used, | 
					
						
							|  |  |  | 						      bufsize - used, | 
					
						
							|  |  |  | 						      wssid_e); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf[used++] = '\n'; | 
					
						
							|  |  |  | 		wlp_remove_neighbor_tmp_info(neighb); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->nbmutex); | 
					
						
							|  |  |  | 	return used; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Show properties of all WSS in neighborhood. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Will trigger a complete discovery of WSS activated by this device and | 
					
						
							|  |  |  |  * its neighbors. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	wlp_discover(wlp); | 
					
						
							|  |  |  | 	return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_neighborhood_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf, | 
					
						
							|  |  |  | 				  size_t bufsize) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid); | 
					
						
							|  |  |  | 	result += scnprintf(buf + result, bufsize - result, " "); | 
					
						
							|  |  |  | 	result += uwb_mac_addr_print(buf + result, bufsize - result, | 
					
						
							|  |  |  | 				     &wss->bcast); | 
					
						
							|  |  |  | 	result += scnprintf(buf + result, bufsize - result, | 
					
						
							|  |  |  | 			    " 0x%02x %u ", wss->hash, wss->secure_status); | 
					
						
							|  |  |  | 	result += wlp_wss_key_print(buf + result, bufsize - result, | 
					
						
							|  |  |  | 				    wss->master_key); | 
					
						
							|  |  |  | 	result += scnprintf(buf + result, bufsize - result, " 0x%02x ", | 
					
						
							|  |  |  | 			    wss->tag); | 
					
						
							|  |  |  | 	result += uwb_mac_addr_print(buf + result, bufsize - result, | 
					
						
							|  |  |  | 				     &wss->virtual_addr); | 
					
						
							|  |  |  | 	result += scnprintf(buf + result, bufsize - result, " %s", wss->name); | 
					
						
							|  |  |  | 	result += scnprintf(buf + result, bufsize - result, | 
					
						
							|  |  |  | 			    "\n\n#WSSID\n#WSS broadcast address\n" | 
					
						
							|  |  |  | 			    "#WSS hash\n#WSS secure status\n" | 
					
						
							|  |  |  | 			    "#WSS master key\n#WSS local tag\n" | 
					
						
							|  |  |  | 			    "#WSS local virtual EUI-48\n#WSS name\n"); | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Show which WSS is activated. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int result = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&wss->mutex)) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	if (wss->state >= WLP_WSS_STATE_ACTIVE) | 
					
						
							|  |  |  | 		result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n"); | 
					
						
							|  |  |  | 	result += scnprintf(buf + result, PAGE_SIZE - result, | 
					
						
							|  |  |  | 			"\n\n" | 
					
						
							|  |  |  | 			"# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT " | 
					
						
							|  |  |  | 			"NAME #create new WSS\n" | 
					
						
							|  |  |  | 			"# echo WSSID [DEV ADDR] #enroll in and activate " | 
					
						
							|  |  |  | 			"existing WSS, can request registrar\n" | 
					
						
							|  |  |  | 			"#\n" | 
					
						
							|  |  |  | 			"# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n" | 
					
						
							|  |  |  | 			"# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n" | 
					
						
							|  |  |  | 			"# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n" | 
					
						
							|  |  |  | 			"# NAME is the text string identifying the WSS\n" | 
					
						
							|  |  |  | 			"# DEV ADDR is the device address of neighbor " | 
					
						
							|  |  |  | 			"that should be registrar. Eg. 32:AB\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&wss->mutex); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_wss_activate_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Create/activate a new WSS or enroll/activate in neighboring WSS | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The user can provide the WSSID of a WSS in which it wants to enroll. | 
					
						
							|  |  |  |  * Only the WSSID is necessary if the WSS have been discovered before. If | 
					
						
							|  |  |  |  * the WSS has not been discovered before, or the user wants to use a | 
					
						
							|  |  |  |  * particular neighbor as its registrar, then the user can also provide a | 
					
						
							|  |  |  |  * device address or the neighbor that will be used as registrar. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * A new WSS is created when the user provides a WSSID, secure status, and | 
					
						
							|  |  |  |  * WSS name. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | ssize_t wlp_wss_activate_store(struct wlp_wss *wss, | 
					
						
							|  |  |  | 			       const char *buf, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result = -EINVAL; | 
					
						
							|  |  |  | 	struct wlp_uuid wssid; | 
					
						
							|  |  |  | 	struct uwb_dev_addr dev; | 
					
						
							|  |  |  | 	struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; | 
					
						
							|  |  |  | 	char name[65]; | 
					
						
							|  |  |  | 	unsigned sec_status, accept; | 
					
						
							|  |  |  | 	memset(name, 0, sizeof(name)); | 
					
						
							|  |  |  | 	result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 			"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 			"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 			"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 			"%02hhx:%02hhx", | 
					
						
							|  |  |  | 			&wssid.data[0] , &wssid.data[1], | 
					
						
							|  |  |  | 			&wssid.data[2] , &wssid.data[3], | 
					
						
							|  |  |  | 			&wssid.data[4] , &wssid.data[5], | 
					
						
							|  |  |  | 			&wssid.data[6] , &wssid.data[7], | 
					
						
							|  |  |  | 			&wssid.data[8] , &wssid.data[9], | 
					
						
							|  |  |  | 			&wssid.data[10], &wssid.data[11], | 
					
						
							|  |  |  | 			&wssid.data[12], &wssid.data[13], | 
					
						
							|  |  |  | 			&wssid.data[14], &wssid.data[15], | 
					
						
							|  |  |  | 			&dev.data[1], &dev.data[0]); | 
					
						
							|  |  |  | 	if (result == 16 || result == 17) { | 
					
						
							|  |  |  | 		result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 				"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 				"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 				"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 				"%u %u %64c", | 
					
						
							|  |  |  | 				&wssid.data[0] , &wssid.data[1], | 
					
						
							|  |  |  | 				&wssid.data[2] , &wssid.data[3], | 
					
						
							|  |  |  | 				&wssid.data[4] , &wssid.data[5], | 
					
						
							|  |  |  | 				&wssid.data[6] , &wssid.data[7], | 
					
						
							|  |  |  | 				&wssid.data[8] , &wssid.data[9], | 
					
						
							|  |  |  | 				&wssid.data[10], &wssid.data[11], | 
					
						
							|  |  |  | 				&wssid.data[12], &wssid.data[13], | 
					
						
							|  |  |  | 				&wssid.data[14], &wssid.data[15], | 
					
						
							|  |  |  | 				&sec_status, &accept, name); | 
					
						
							|  |  |  | 		if (result == 16) | 
					
						
							|  |  |  | 			result = wlp_wss_enroll_activate(wss, &wssid, &bcast); | 
					
						
							|  |  |  | 		else if (result == 19) { | 
					
						
							|  |  |  | 			sec_status = sec_status == 0 ? 0 : 1; | 
					
						
							|  |  |  | 			accept = accept == 0 ? 0 : 1; | 
					
						
							|  |  |  | 			/* We read name using %c, so the newline needs to be
 | 
					
						
							|  |  |  | 			 * removed */ | 
					
						
							|  |  |  | 			if (strlen(name) != sizeof(name) - 1) | 
					
						
							|  |  |  | 				name[strlen(name) - 1] = '\0'; | 
					
						
							|  |  |  | 			result = wlp_wss_create_activate(wss, &wssid, name, | 
					
						
							|  |  |  | 							 sec_status, accept); | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			result = -EINVAL; | 
					
						
							|  |  |  | 	} else if (result == 18) | 
					
						
							|  |  |  | 		result = wlp_wss_enroll_activate(wss, &wssid, &dev); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		result = -EINVAL; | 
					
						
							|  |  |  | 	return result < 0 ? result : size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_wss_activate_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Show the UUID of this host | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | ssize_t wlp_uuid_show(struct wlp *wlp, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid); | 
					
						
							|  |  |  | 	buf[result++] = '\n'; | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_uuid_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Store a new UUID for this host | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * According to the spec this should be encoded as an octet string in the | 
					
						
							|  |  |  |  * order the octets are shown in string representation in RFC 4122 (WLP | 
					
						
							|  |  |  |  * 0.99 [Table 6]) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * We do not check value provided by user. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result; | 
					
						
							|  |  |  | 	struct wlp_uuid uuid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 			"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 			"%02hhx %02hhx %02hhx %02hhx " | 
					
						
							|  |  |  | 			"%02hhx %02hhx %02hhx %02hhx ", | 
					
						
							|  |  |  | 			&uuid.data[0] , &uuid.data[1], | 
					
						
							|  |  |  | 			&uuid.data[2] , &uuid.data[3], | 
					
						
							|  |  |  | 			&uuid.data[4] , &uuid.data[5], | 
					
						
							|  |  |  | 			&uuid.data[6] , &uuid.data[7], | 
					
						
							|  |  |  | 			&uuid.data[8] , &uuid.data[9], | 
					
						
							|  |  |  | 			&uuid.data[10], &uuid.data[11], | 
					
						
							|  |  |  | 			&uuid.data[12], &uuid.data[13], | 
					
						
							|  |  |  | 			&uuid.data[14], &uuid.data[15]); | 
					
						
							|  |  |  | 	if (result != 16) { | 
					
						
							|  |  |  | 		result = -EINVAL; | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	wlp->uuid = uuid; | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result < 0 ? result : size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_uuid_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Show contents of members of device information structure | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define wlp_dev_info_show(type)						\
 | 
					
						
							|  |  |  | ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf)		\ | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	ssize_t result = 0;						\ | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex);					\ | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) {					\ | 
					
						
							|  |  |  | 		result = __wlp_setup_device_info(wlp);			\ | 
					
						
							|  |  |  | 		if (result < 0)						\ | 
					
						
							|  |  |  | 			goto out;					\ | 
					
						
							|  |  |  | 	}								\ | 
					
						
							|  |  |  | 	result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\ | 
					
						
							|  |  |  | out:									\ | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex);					\ | 
					
						
							|  |  |  | 	return result;							\ | 
					
						
							|  |  |  | }									\ | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_##type##_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | wlp_dev_info_show(name) | 
					
						
							|  |  |  | wlp_dev_info_show(model_name) | 
					
						
							|  |  |  | wlp_dev_info_show(model_nr) | 
					
						
							|  |  |  | wlp_dev_info_show(manufacturer) | 
					
						
							|  |  |  | wlp_dev_info_show(serial) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Store contents of members of device information structure | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define wlp_dev_info_store(type, len)					\
 | 
					
						
							|  |  |  | ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\ | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	ssize_t result;							\ | 
					
						
							|  |  |  | 	char format[10];						\ | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex);					\ | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) {					\ | 
					
						
							|  |  |  | 		result = __wlp_alloc_device_info(wlp);			\ | 
					
						
							|  |  |  | 		if (result < 0)						\ | 
					
						
							|  |  |  | 			goto out;					\ | 
					
						
							|  |  |  | 	}								\ | 
					
						
							|  |  |  | 	memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type));	\ | 
					
						
							|  |  |  | 	sprintf(format, "%%%uc", len);					\ | 
					
						
							|  |  |  | 	result = sscanf(buf, format, wlp->dev_info->type);		\ | 
					
						
							|  |  |  | out:									\ | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex);					\ | 
					
						
							|  |  |  | 	return result < 0 ? result : size;				\ | 
					
						
							|  |  |  | }									\ | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_##type##_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | wlp_dev_info_store(name, 32) | 
					
						
							|  |  |  | wlp_dev_info_store(manufacturer, 64) | 
					
						
							|  |  |  | wlp_dev_info_store(model_name, 32) | 
					
						
							|  |  |  | wlp_dev_info_store(model_nr, 32) | 
					
						
							|  |  |  | wlp_dev_info_store(serial, 32) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | const char *__wlp_dev_category[] = { | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_COMPUTER] = "Computer", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_INPUT] = "Input device", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or " | 
					
						
							|  |  |  | 					      "Copier", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_CAMERA] = "Camera", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_STORAGE] = "Storage Network", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_DISPLAY] = "Display", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_MULTIM] = "Multimedia device", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_GAMING] = "Gaming device", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_TELEPHONE] = "Telephone", | 
					
						
							|  |  |  | 	[WLP_DEV_CAT_OTHER] = "Other", | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | const char *wlp_dev_category_str(unsigned cat) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) | 
					
						
							|  |  |  | 	    || cat == WLP_DEV_CAT_OTHER) | 
					
						
							|  |  |  | 		return __wlp_dev_category[cat]; | 
					
						
							|  |  |  | 	return "unknown category"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result = 0; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_setup_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = scnprintf(buf, PAGE_SIZE, "%s\n", | 
					
						
							|  |  |  | 		  wlp_dev_category_str(wlp->dev_info->prim_dev_type.category)); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf, | 
					
						
							|  |  |  | 				    size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result; | 
					
						
							|  |  |  | 	u16 cat; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_alloc_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = sscanf(buf, "%hu", &cat); | 
					
						
							|  |  |  | 	if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) | 
					
						
							|  |  |  | 	    || cat == WLP_DEV_CAT_OTHER) | 
					
						
							|  |  |  | 		wlp->dev_info->prim_dev_type.category = cat; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		result = -EINVAL; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result < 0 ? result : size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result = 0; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_setup_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n", | 
					
						
							|  |  |  | 			   wlp->dev_info->prim_dev_type.OUI[0], | 
					
						
							|  |  |  | 			   wlp->dev_info->prim_dev_type.OUI[1], | 
					
						
							|  |  |  | 			   wlp->dev_info->prim_dev_type.OUI[2]); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result; | 
					
						
							|  |  |  | 	u8 OUI[3]; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_alloc_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = sscanf(buf, "%hhx:%hhx:%hhx", | 
					
						
							|  |  |  | 			&OUI[0], &OUI[1], &OUI[2]); | 
					
						
							|  |  |  | 	if (result != 3) { | 
					
						
							|  |  |  | 		result = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI)); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result < 0 ? result : size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result = 0; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_setup_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = scnprintf(buf, PAGE_SIZE, "%u\n", | 
					
						
							|  |  |  | 			   wlp->dev_info->prim_dev_type.OUIsubdiv); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf, | 
					
						
							|  |  |  | 				   size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result; | 
					
						
							|  |  |  | 	unsigned sub; | 
					
						
							|  |  |  | 	u8 max_sub = ~0; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_alloc_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = sscanf(buf, "%u", &sub); | 
					
						
							|  |  |  | 	if (sub <= max_sub) | 
					
						
							|  |  |  | 		wlp->dev_info->prim_dev_type.OUIsubdiv = sub; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		result = -EINVAL; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result < 0 ? result : size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result = 0; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_setup_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = scnprintf(buf, PAGE_SIZE, "%u\n", | 
					
						
							|  |  |  | 			   wlp->dev_info->prim_dev_type.subID); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf, | 
					
						
							|  |  |  | 				  size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ssize_t result; | 
					
						
							|  |  |  | 	unsigned sub; | 
					
						
							|  |  |  | 	__le16 max_sub = ~0; | 
					
						
							|  |  |  | 	mutex_lock(&wlp->mutex); | 
					
						
							|  |  |  | 	if (wlp->dev_info == NULL) { | 
					
						
							|  |  |  | 		result = __wlp_alloc_device_info(wlp); | 
					
						
							|  |  |  | 		if (result < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	result = sscanf(buf, "%u", &sub); | 
					
						
							|  |  |  | 	if (sub <= max_sub) | 
					
						
							|  |  |  | 		wlp->dev_info->prim_dev_type.subID = sub; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		result = -EINVAL; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&wlp->mutex); | 
					
						
							|  |  |  | 	return result < 0 ? result : size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Subsystem implementation for interaction with individual WSS via sysfs | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj)
 | 
					
						
							|  |  |  | #define attr_to_wlp_wss_attr(_attr) \
 | 
					
						
							|  |  |  | 	container_of(_attr, struct wlp_wss_attribute, attr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Sysfs subsystem: forward read calls | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Sysfs operation for forwarding read call to the show method of the | 
					
						
							|  |  |  |  * attribute owner | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr, | 
					
						
							|  |  |  | 			  char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); | 
					
						
							|  |  |  | 	struct wlp_wss *wss = kobj_to_wlp_wss(kobj); | 
					
						
							|  |  |  | 	ssize_t ret = -EIO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (wss_attr->show) | 
					
						
							|  |  |  | 		ret = wss_attr->show(wss, buf); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Sysfs subsystem: forward write calls | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Sysfs operation for forwarding write call to the store method of the | 
					
						
							|  |  |  |  * attribute owner | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr, | 
					
						
							|  |  |  | 			   const char *buf, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); | 
					
						
							|  |  |  | 	struct wlp_wss *wss = kobj_to_wlp_wss(kobj); | 
					
						
							|  |  |  | 	ssize_t ret = -EIO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (wss_attr->store) | 
					
						
							|  |  |  | 		ret = wss_attr->store(wss, buf, count); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | struct sysfs_ops wss_sysfs_ops = { | 
					
						
							|  |  |  | 	.show	= wlp_wss_attr_show, | 
					
						
							|  |  |  | 	.store	= wlp_wss_attr_store, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct kobj_type wss_ktype = { | 
					
						
							|  |  |  | 	.release	= wlp_wss_release, | 
					
						
							|  |  |  | 	.sysfs_ops	= &wss_sysfs_ops, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Sysfs files for individual WSS | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Print static properties of this WSS | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The name of a WSS may not be null teminated. It's max size is 64 bytes | 
					
						
							|  |  |  |  * so we copy it to a larger array just to make sure we print sane data. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int result = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&wss->mutex)) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); | 
					
						
							|  |  |  | 	mutex_unlock(&wss->mutex); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Print all connected members of this WSS | 
					
						
							|  |  |  |  * The EDA cache contains all members of WSS neighborhood. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct wlp *wlp = container_of(wss, struct wlp, wss); | 
					
						
							|  |  |  | 	return wlp_eda_show(wlp, buf); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | const char *__wlp_strstate[] = { | 
					
						
							|  |  |  | 	"none", | 
					
						
							|  |  |  | 	"partially enrolled", | 
					
						
							|  |  |  | 	"enrolled", | 
					
						
							|  |  |  | 	"active", | 
					
						
							|  |  |  | 	"connected", | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *wlp_wss_strstate(unsigned state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (state >= ARRAY_SIZE(__wlp_strstate)) | 
					
						
							|  |  |  | 		return "unknown state"; | 
					
						
							|  |  |  | 	return __wlp_strstate[state]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Print current state of this WSS | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int result = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&wss->mutex)) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	result = scnprintf(buf, PAGE_SIZE, "%s\n", | 
					
						
							|  |  |  | 			   wlp_wss_strstate(wss->state)); | 
					
						
							|  |  |  | 	mutex_unlock(&wss->mutex); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | struct attribute *wss_attrs[] = { | 
					
						
							|  |  |  | 	&wss_attr_properties.attr, | 
					
						
							|  |  |  | 	&wss_attr_members.attr, | 
					
						
							|  |  |  | 	&wss_attr_state.attr, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct attribute_group wss_attr_group = { | 
					
						
							|  |  |  | 	.name = NULL,	/* we want them in the same directory */ | 
					
						
							|  |  |  | 	.attrs = wss_attrs, | 
					
						
							|  |  |  | }; |