| 
					
				 | 
			
			
				@@ -39,7 +39,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "routerboot.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-#define RB_HARDCONFIG_VER		"0.05" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define RB_HARDCONFIG_VER		"0.06" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #define RB_HC_PR_PFX			"[rb_hardconfig] " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /* ID values for hardware settings */ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -76,6 +76,17 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #define RB_HW_OPT_HAS_TS_FOR_ADC	BIT(22) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #define RB_HW_OPT_HAS_PLC		BIT(29) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Tag ID values for ERD data. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Mikrotik used to pack all calibration data under a single tag id 0x1, but 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * recently switched to a new scheme where each radio calibration gets a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * separate tag. The new scheme has tag id bit 15 always set and seems to be 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * mutually exclusive with the old scheme. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define RB_WLAN_ERD_ID_SOLO		0x0001 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define RB_WLAN_ERD_ID_MULTI_8001	0x8001 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define RB_WLAN_ERD_ID_MULTI_8201	0x8201 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static struct kobject *hc_kobj; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static u8 *hc_buf;		// ro buffer after init(): no locking required 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static size_t hc_buflen; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -351,10 +362,22 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				     loff_t off, size_t count); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static struct hc_wlan_attr { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const u16 erd_tag_id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	struct bin_attribute battr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	u16 pld_ofs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	u16 pld_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} hc_wlandata_battr = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} hc_wd_multi_battrs[] = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		.erd_tag_id = RB_WLAN_ERD_ID_MULTI_8001, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		.battr = __BIN_ATTR(data_0, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		.erd_tag_id = RB_WLAN_ERD_ID_MULTI_8201, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		.battr = __BIN_ATTR(data_2, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static struct hc_wlan_attr hc_wd_solo_battr = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	.erd_tag_id = RB_WLAN_ERD_ID_SOLO, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	.battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -426,19 +449,19 @@ static struct hc_attr { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * that magic number the payload itself contains a routerboot tag node 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * locating the LZO-compressed calibration data at id 0x1. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * locating the LZO-compressed calibration data. So far this scheme is only 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * known to use a single tag at id 0x1. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int hc_wlan_data_unpack_erd(const u16 tag_id, const u8 *inbuf, size_t inlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				   void *outbuf, size_t *outlen) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	u16 lzo_ofs, lzo_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	/* Find embedded tag */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	ret = routerboot_tag_find(inbuf, inlen, 0x1,	// always id 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				  &lzo_ofs, &lzo_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ret = routerboot_tag_find(inbuf, inlen, tag_id, &lzo_ofs, &lzo_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if (ret) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		pr_debug(RB_HC_PR_PFX "ERD data not found\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		pr_debug(RB_HC_PR_PFX "no ERD data for id 0x%04x\n", tag_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		goto fail; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -461,10 +484,10 @@ fail: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * that magic number is a payload that must be appended to the hc_lzor_prefix, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * the resulting blob is LZO-compressed. In the LZO decompression result, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * the RB_MAGIC_ERD magic number (aligned) must be located. Following that 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * magic, there is a routerboot tag node (id 0x1) locating the RLE-encoded 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * magic, there is one or more routerboot tag node(s) locating the RLE-encoded 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * calibration data payload. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t inlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				    void *outbuf, size_t *outlen) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	u16 rle_ofs, rle_len; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -492,10 +515,8 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if (ret) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if (LZO_E_INPUT_NOT_CONSUMED == ret) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			 * The tag length appears to always be aligned (probably 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			 * because it is the "root" RB_ID_WLAN_DATA tag), thus 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			 * the LZO payload may be padded, which can trigger a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			 * spurious error which we ignore here. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			 * The tag length is always aligned thus the LZO payload may be padded, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			 * which can trigger a spurious error which we ignore here. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} else { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -520,9 +541,9 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	templen -= (u8 *)needle - tempbuf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	/* Past magic. Look for tag node */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ret = routerboot_tag_find((u8 *)needle, templen, tag_id, &rle_ofs, &rle_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if (ret) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		pr_debug(RB_HC_PR_PFX "LZOR: no RLE data for id 0x%04x\n", tag_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		goto fail; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -542,7 +563,7 @@ fail: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static int hc_wlan_data_unpack(const size_t tofs, size_t tlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			       void *outbuf, size_t *outlen) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	const u8 *lbuf; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -562,23 +583,25 @@ static int hc_wlan_data_unpack(const size_t tofs, size_t tlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		/* Skip magic */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		lbuf += sizeof(magic); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		tlen -= sizeof(magic); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ret = hc_wlan_data_unpack_lzor(lbuf, tlen, outbuf, outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ret = hc_wlan_data_unpack_lzor(tag_id, lbuf, tlen, outbuf, outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	case RB_MAGIC_ERD: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		/* Skip magic */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		lbuf += sizeof(magic); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		tlen -= sizeof(magic); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ret = hc_wlan_data_unpack_erd(tag_id, lbuf, tlen, outbuf, outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		 * If the RB_ID_WLAN_DATA payload doesn't start with a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		 * magic number, the payload itself is the raw RLE-encoded 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		 * calibration data. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * calibration data. Only RB_WLAN_ERD_ID_SOLO makes sense here. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if (ret) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if (RB_WLAN_ERD_ID_SOLO == tag_id) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if (ret) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -633,7 +656,7 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if (!outbuf) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return -ENOMEM; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ret = hc_wlan_data_unpack(hc_wattr->erd_tag_id, hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if (ret) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		kfree(outbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return ret; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -655,14 +678,17 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 int __init rb_hardconfig_init(struct kobject *rb_kobj) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	struct kobject *hc_wlan_kobj; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	struct mtd_info *mtd; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	size_t bytes_read, buflen; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	size_t bytes_read, buflen, outlen; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	const u8 *buf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	int i, ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	void *outbuf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	int i, j, ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	u32 magic; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	hc_buf = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	hc_kobj = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	hc_wlan_kobj = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// TODO allow override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -713,15 +739,62 @@ int __init rb_hardconfig_init(struct kobject *rb_kobj) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		/* Account for skipped magic */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		hc_attrs[i].pld_ofs += sizeof(magic); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		/* Special case RB_ID_WLAN_DATA to prep and create the binary attribute */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * Special case RB_ID_WLAN_DATA to prep and create the binary attribute. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * We first check if the data is "old style" within a single tag (or no tag at all): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * If it is we publish this single blob as a binary attribute child of hc_kobj to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * preserve backward compatibility. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * If it isn't and instead uses multiple ERD tags, we create a subfolder and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * publish the known ones there. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if ((RB_ID_WLAN_DATA == hc_attrs[i].tag_id) && hc_attrs[i].pld_len) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			hc_wlandata_battr.pld_ofs = hc_attrs[i].pld_ofs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			hc_wlandata_battr.pld_len = hc_attrs[i].pld_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if (ret) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				       hc_wlandata_battr.battr.attr.name, ret); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			outlen = RB_ART_SIZE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			outbuf = kmalloc(outlen, GFP_KERNEL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if (!outbuf) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				pr_warn(RB_HC_PR_PFX "Out of memory parsing WLAN tag\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			/* Test ID_SOLO first, if found: done */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			ret = hc_wlan_data_unpack(RB_WLAN_ERD_ID_SOLO, hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if (!ret) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				hc_wd_solo_battr.pld_ofs = hc_attrs[i].pld_ofs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				hc_wd_solo_battr.pld_len = hc_attrs[i].pld_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				ret = sysfs_create_bin_file(hc_kobj, &hc_wd_solo_battr.battr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (ret) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						hc_wd_solo_battr.battr.attr.name, ret); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			/* Otherwise, create "wlan_data" subtree and publish known data */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				hc_wlan_kobj = kobject_create_and_add("wlan_data", hc_kobj); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (!hc_wlan_kobj) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					kfree(outbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					pr_warn(RB_HC_PR_PFX "Could not create wlan_data sysfs folder\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				for (j = 0; j < ARRAY_SIZE(hc_wd_multi_battrs); j++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					outlen = RB_ART_SIZE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					ret = hc_wlan_data_unpack(hc_wd_multi_battrs[j].erd_tag_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+								  hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					if (ret) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						hc_wd_multi_battrs[j].pld_ofs = hc_wd_multi_battrs[j].pld_len = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					hc_wd_multi_battrs[j].pld_ofs = hc_attrs[i].pld_ofs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					hc_wd_multi_battrs[j].pld_len = hc_attrs[i].pld_len; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					ret = sysfs_create_bin_file(hc_wlan_kobj, &hc_wd_multi_battrs[j].battr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					if (ret) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						pr_warn(RB_HC_PR_PFX "Could not create wlan_data/%s sysfs entry (%d)\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							hc_wd_multi_battrs[j].battr.attr.name, ret); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			kfree(outbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		/* All other tags are published via standard attributes */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		else { 
			 |