[PATCH] ieee80211: Add 802.11h information element parsing

Added default handlers for various 802.11h DFS and TPC information
elements.  Moved all information elements into single location (called
from two places).  Added debug message with information on unparsed IEs
if debug_level set.  Added code to reset network IBSS DFS information
when appropriate.  Added code to invoke driver callback for 802.11h
ACTION STYPE.  Changed a few printk's to IEEE80211_DEBUG_MGMT.

Signed-off-by: James Ketrenos <jketreno@linux.intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index 4165da7..a4ebc22 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -937,6 +937,45 @@
 	return rc;
 }
 
+#ifdef CONFIG_IEEE80211_DEBUG
+#define MFIE_STRING(x) case MFIE_TYPE_ ##x: return #x
+
+static const char *get_info_element_string(u16 id)
+{
+	switch (id) {
+		MFIE_STRING(SSID);
+		MFIE_STRING(RATES);
+		MFIE_STRING(FH_SET);
+		MFIE_STRING(DS_SET);
+		MFIE_STRING(CF_SET);
+		MFIE_STRING(TIM);
+		MFIE_STRING(IBSS_SET);
+		MFIE_STRING(COUNTRY);
+		MFIE_STRING(HOP_PARAMS);
+		MFIE_STRING(HOP_TABLE);
+		MFIE_STRING(REQUEST);
+		MFIE_STRING(CHALLENGE);
+		MFIE_STRING(POWER_CONSTRAINT);
+		MFIE_STRING(POWER_CAPABILITY);
+		MFIE_STRING(TPC_REQUEST);
+		MFIE_STRING(TPC_REPORT);
+		MFIE_STRING(SUPP_CHANNELS);
+		MFIE_STRING(CSA);
+		MFIE_STRING(MEASURE_REQUEST);
+		MFIE_STRING(MEASURE_REPORT);
+		MFIE_STRING(QUIET);
+		MFIE_STRING(IBSS_DFS);
+		MFIE_STRING(ERP_INFO);
+		MFIE_STRING(RSN);
+		MFIE_STRING(RATES_EX);
+		MFIE_STRING(GENERIC);
+		MFIE_STRING(QOS_PARAMETER);
+	default:
+		return "UNKNOWN";
+	}
+}
+#endif
+
 static int ieee80211_parse_info_param(struct ieee80211_info_element
 				      *info_element, u16 length,
 				      struct ieee80211_network *network)
@@ -1100,10 +1139,49 @@
 			printk(KERN_ERR
 			       "QoS Error need to parse QOS_PARAMETER IE\n");
 			break;
+			/* 802.11h */
+		case MFIE_TYPE_POWER_CONSTRAINT:
+			network->power_constraint = info_element->data[0];
+			network->flags |= NETWORK_HAS_POWER_CONSTRAINT;
+			break;
+
+		case MFIE_TYPE_CSA:
+			network->power_constraint = info_element->data[0];
+			network->flags |= NETWORK_HAS_CSA;
+			break;
+
+		case MFIE_TYPE_QUIET:
+			network->quiet.count = info_element->data[0];
+			network->quiet.period = info_element->data[1];
+			network->quiet.duration = info_element->data[2];
+			network->quiet.offset = info_element->data[3];
+			network->flags |= NETWORK_HAS_QUIET;
+			break;
+
+		case MFIE_TYPE_IBSS_DFS:
+			if (network->ibss_dfs)
+				break;
+			network->ibss_dfs =
+			    kmalloc(info_element->len, GFP_ATOMIC);
+			if (!network->ibss_dfs)
+				return 1;
+			memcpy(network->ibss_dfs, info_element->data,
+			       info_element->len);
+			network->flags |= NETWORK_HAS_IBSS_DFS;
+			break;
+
+		case MFIE_TYPE_TPC_REPORT:
+			network->tpc_report.transmit_power =
+			    info_element->data[0];
+			network->tpc_report.link_margin = info_element->data[1];
+			network->flags |= NETWORK_HAS_TPC_REPORT;
+			break;
 
 		default:
-			IEEE80211_DEBUG_MGMT("unsupported IE %d\n",
-					     info_element->id);
+			IEEE80211_DEBUG_MGMT
+			    ("Unsupported info element: %s (%d)\n",
+			     get_info_element_string(info_element->id),
+			     info_element->id);
 			break;
 		}
 
@@ -1119,7 +1197,9 @@
 static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct ieee80211_assoc_response
 				       *frame, struct ieee80211_rx_stats *stats)
 {
-	struct ieee80211_network network_resp;
+	struct ieee80211_network network_resp = {
+		.ibss_dfs = NULL,
+	};
 	struct ieee80211_network *network = &network_resp;
 	struct net_device *dev = ieee->dev;
 
@@ -1262,6 +1342,9 @@
 	int qos_active;
 	u8 old_param;
 
+	ieee80211_network_reset(dst);
+	dst->ibss_dfs = src->ibss_dfs;
+
 	memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats));
 	dst->capability = src->capability;
 	memcpy(dst->rates, src->rates, src->rates_len);
@@ -1323,7 +1406,9 @@
 						    *stats)
 {
 	struct net_device *dev = ieee->dev;
-	struct ieee80211_network network;
+	struct ieee80211_network network = {
+		.ibss_dfs = NULL,
+	};
 	struct ieee80211_network *target;
 	struct ieee80211_network *oldest = NULL;
 #ifdef CONFIG_IEEE80211_DEBUG
@@ -1398,6 +1483,7 @@
 					     escape_essid(target->ssid,
 							  target->ssid_len),
 					     MAC_ARG(target->bssid));
+			ieee80211_network_reset(target);
 		} else {
 			/* Otherwise just pull from the free list */
 			target = list_entry(ieee->network_free_list.next,
@@ -1416,6 +1502,7 @@
 				     "BEACON" : "PROBE RESPONSE");
 #endif
 		memcpy(target, &network, sizeof(*target));
+		network.ibss_dfs = NULL;
 		list_add_tail(&target->list, &ieee->network_list);
 	} else {
 		IEEE80211_DEBUG_SCAN("Updating '%s' (" MAC_FMT ") via %s.\n",
@@ -1427,6 +1514,7 @@
 						frame_ctl)) ?
 				     "BEACON" : "PROBE RESPONSE");
 		update_network(target, &network);
+		network.ibss_dfs = NULL;
 	}
 
 	spin_unlock_irqrestore(&ieee->lock, flags);
@@ -1511,10 +1599,19 @@
 					      header);
 		break;
 
+	case IEEE80211_STYPE_ACTION:
+		IEEE80211_DEBUG_MGMT("ACTION\n");
+		if (ieee->handle_action)
+			ieee->handle_action(ieee->dev,
+					    (struct ieee80211_action *)
+					    header, stats);
+		break;
+
 	case IEEE80211_STYPE_DEAUTH:
-		printk("DEAUTH from AP\n");
+		IEEE80211_DEBUG_MGMT("DEAUTH\n");
 		if (ieee->handle_deauth != NULL)
-			ieee->handle_deauth(ieee->dev, (struct ieee80211_deauth *)
+			ieee->handle_deauth(ieee->dev,
+					    (struct ieee80211_deauth *)
 					    header);
 		break;
 	default: