Netdev List
 help / color / mirror / Atom feed
* [WEXT 11/12]: Create IW_REQUEST_FLAG_COMPAT and set it as needed.
From: David Miller @ 2007-12-22  4:57 UTC (permalink / raw)
  To: linux-wireless; +Cc: netdev


[WEXT]: Create IW_REQUEST_FLAG_COMPAT and set it as needed.

Now low-level WEXT ioctl handlers can do compat handling
when necessary.

Signed-off-by: David S. Miller <davem@davemloft.net>
---
 include/net/iw_handler.h |    2 +-
 net/wireless/wext.c      |   73 +++++++++++++++++++++-------------------------
 2 files changed, 34 insertions(+), 41 deletions(-)

diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h
index 369d50e..c99a8ee 100644
--- a/include/net/iw_handler.h
+++ b/include/net/iw_handler.h
@@ -256,7 +256,7 @@
 #define EIWCOMMIT	EINPROGRESS
 
 /* Flags available in struct iw_request_info */
-#define IW_REQUEST_FLAG_NONE	0x0000	/* No flag so far */
+#define IW_REQUEST_FLAG_COMPAT	0x0001	/* Compat ioctl call */
 
 /* Type of headers we know about (basically union iwreq_data) */
 #define IW_HEADER_TYPE_NULL	0	/* Not available */
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index 5869b70..b7f5973 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -866,10 +866,10 @@ out:
 static int ioctl_standard_call(struct net_device *	dev,
 			       struct iwreq *		iwr,
 			       unsigned int		cmd,
+			       struct iw_request_info	*info,
 			       iw_handler		handler)
 {
 	const struct iw_ioctl_description *	descr;
-	struct iw_request_info			info;
 	int					ret = -EINVAL;
 
 	/* Get the description of the IOCTL */
@@ -877,15 +877,11 @@ static int ioctl_standard_call(struct net_device *	dev,
 		return -EOPNOTSUPP;
 	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
 
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
 	/* Check if we have a pointer to user space data or not */
 	if (descr->header_type != IW_HEADER_TYPE_POINT) {
 
 		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, &(iwr->u), NULL);
+		ret = handler(dev, info, &(iwr->u), NULL);
 
 		/* Generate an event to notify listeners of the change */
 		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
@@ -893,7 +889,7 @@ static int ioctl_standard_call(struct net_device *	dev,
 			wireless_send_event(dev, cmd, &(iwr->u), NULL);
 	} else {
 		ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
-					      handler, dev, &info);
+					      handler, dev, info);
 	}
 
 	/* Call commit handler if needed and defined */
@@ -1016,25 +1012,21 @@ out:
 }
 
 static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
-			      unsigned int cmd, iw_handler handler)
+			      unsigned int cmd, struct iw_request_info *info,
+			      iw_handler handler)
 {
 	int extra_size = 0, ret = -EINVAL;
 	const struct iw_priv_args *descr;
-	struct iw_request_info info;
 
 	extra_size = get_priv_descr_and_size(dev, cmd, &descr);
 
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
 	/* Check if we have a pointer to user space data or not. */
 	if (extra_size == 0) {
 		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+		ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
 	} else {
 		ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
-					     handler, dev, &info, extra_size);
+					     handler, dev, info, extra_size);
 	}
 
 	/* Call commit handler if needed and defined */
@@ -1046,7 +1038,8 @@ static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
 
 /* ---------------------------------------------------------------- */
 typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
-			       unsigned int, iw_handler);
+			       unsigned int, struct iw_request_info *,
+			       iw_handler);
 
 /*
  * Main IOCTl dispatcher.
@@ -1054,6 +1047,7 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
  */
 static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
 				  unsigned int cmd,
+				  struct iw_request_info *info,
 				  wext_ioctl_func standard,
 				  wext_ioctl_func private)
 {
@@ -1072,11 +1066,11 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
 	 * Note that 'cmd' is already filtered in dev_ioctl() with
 	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
 	if (cmd == SIOCGIWSTATS)
-		return standard(dev, iwr, cmd,
+		return standard(dev, iwr, cmd, info,
 				&iw_handler_get_iwstats);
 
 	if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
-		return standard(dev, iwr, cmd,
+		return standard(dev, iwr, cmd, info,
 				&iw_handler_get_private);
 
 	/* Basic check */
@@ -1088,9 +1082,9 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
 	if (handler) {
 		/* Standard and private are not the same */
 		if (cmd < SIOCIWFIRSTPRIV)
-			return standard(dev, iwr, cmd, handler);
+			return standard(dev, iwr, cmd, info, handler);
 		else
-			return private(dev, iwr, cmd, handler);
+			return private(dev, iwr, cmd, info, handler);
 	}
 	/* Old driver API : call driver ioctl handler */
 	if (dev->do_ioctl)
@@ -1112,7 +1106,7 @@ static int wext_permission_check(unsigned int cmd)
 
 /* entry point from dev ioctl */
 static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
-			       unsigned int cmd,
+			       unsigned int cmd, struct iw_request_info *info,
 			       wext_ioctl_func standard,
 			       wext_ioctl_func private)
 {
@@ -1123,7 +1117,7 @@ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
 
 	dev_load(net, ifr->ifr_name);
 	rtnl_lock();
-	ret = wireless_process_ioctl(net, ifr, cmd, standard, private);
+	ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private);
 	rtnl_unlock();
 
 	return ret;
@@ -1132,10 +1126,12 @@ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
 int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
 		      void __user *arg)
 {
-	int ret = wext_ioctl_dispatch(net, ifr, cmd,
-				      ioctl_standard_call,
-				      ioctl_private_call);
+	struct iw_request_info info = { .cmd = cmd, .flags = 0 };
+	int ret;
 
+	ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
+				  ioctl_standard_call,
+				  ioctl_private_call);
 	if (ret > 0 &&
 	    IW_IS_GET(cmd) &&
 	    copy_to_user(arg, ifr, sizeof(struct iwreq)))
@@ -1148,28 +1144,25 @@ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
 static int compat_standard_call(struct net_device *	dev,
 				struct iwreq *		iwr,
 				unsigned int		cmd,
+				struct iw_request_info	*info,
 				iw_handler		handler)
 {
 	const struct iw_ioctl_description *descr;
 	struct compat_iw_point *iwp_compat;
-	struct iw_request_info info;
 	struct iw_point iwp;
 	int err;
 
 	descr = standard_ioctl + (cmd - SIOCIWFIRST);
 
 	if (descr->header_type != IW_HEADER_TYPE_POINT)
-		return ioctl_standard_call(dev, iwr, cmd, handler);
+		return ioctl_standard_call(dev, iwr, cmd, info, handler);
 
 	iwp_compat = (struct compat_iw_point *) &iwr->u.data;
 	iwp.pointer = compat_ptr(iwp_compat->pointer);
 	iwp.length = iwp_compat->length;
 	iwp.flags = iwp_compat->flags;
 
-	info.cmd = cmd;
-	info.flags = 0;
-
-	err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, &info);
+	err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info);
 
 	iwp_compat->pointer = ptr_to_compat(iwp.pointer);
 	iwp_compat->length = iwp.length;
@@ -1179,22 +1172,18 @@ static int compat_standard_call(struct net_device *	dev,
 }
 
 static int compat_private_call(struct net_device *dev, struct iwreq *iwr,
-			       unsigned int cmd, iw_handler handler)
+			       unsigned int cmd, struct iw_request_info *info,
+			       iw_handler handler)
 {
 	const struct iw_priv_args *descr;
-	struct iw_request_info info;
 	int ret, extra_size;
 
 	extra_size = get_priv_descr_and_size(dev, cmd, &descr);
 
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
 	/* Check if we have a pointer to user space data or not. */
 	if (extra_size == 0) {
 		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+		ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
 	} else {
 		struct compat_iw_point *iwp_compat;
 		struct iw_point iwp;
@@ -1205,7 +1194,7 @@ static int compat_private_call(struct net_device *dev, struct iwreq *iwr,
 		iwp.flags = iwp_compat->flags;
 
 		ret = ioctl_private_iw_point(&iwp, cmd, descr,
-					     handler, dev, &info, extra_size);
+					     handler, dev, info, extra_size);
 
 		iwp_compat->pointer = ptr_to_compat(iwp.pointer);
 		iwp_compat->length = iwp.length;
@@ -1223,6 +1212,7 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
 			     unsigned long arg)
 {
 	void __user *argp = (void __user *)arg;
+	struct iw_request_info info;
 	struct iwreq iwr;
 	char *colon;
 	int ret;
@@ -1235,7 +1225,10 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
 	if (colon)
 		*colon = 0;
 
-	ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd,
+	info.cmd = cmd;
+	info.flags = IW_REQUEST_FLAG_COMPAT;
+
+	ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info,
 				  compat_standard_call,
 				  compat_private_call);
 
-- 
1.5.4.rc1


^ permalink raw reply related

* [WEXT 12/12]: Emit event stream compat iw_point objects correctly.
From: David Miller @ 2007-12-22  4:58 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA


[WEXT]: Emit event stream compat iw_point objects correctly.

Three major portions to this change:

1) Add IW_EV_COMPAT_POINT_LEN helper define.

2) Add iw_request_info argument to iwe_stream_add_point() and
   iwe_stream_check_add_point(), and use it to size the event
   and pointer lengths correctly depending upon whether
   IW_REQUEST_FLAG_COMPAT is set or not.

3) The mechanical transformations to the drivers and wireless stack
   bits to get the iw_request_info passed down into the routines
   modified in #2.

Signed-off-by: David S. Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
---
 drivers/net/wireless/airo.c                |   16 ++++++++++------
 drivers/net/wireless/atmel.c               |    4 ++--
 drivers/net/wireless/hostap/hostap.h       |    3 ++-
 drivers/net/wireless/hostap/hostap_ap.c    |   13 ++++++++-----
 drivers/net/wireless/hostap/hostap_ioctl.c |   28 ++++++++++++++++------------
 drivers/net/wireless/libertas/scan.c       |   19 +++++++++++--------
 drivers/net/wireless/orinoco.c             |    7 ++++---
 drivers/net/wireless/prism54/isl_ioctl.c   |   20 +++++++++++++-------
 drivers/net/wireless/wl3501_cs.c           |    6 ++++--
 drivers/net/wireless/zd1201.c              |    5 +++--
 include/linux/wireless.h                   |    6 ++++++
 include/net/iw_handler.h                   |   28 ++++++++++++++++++++++++----
 net/ieee80211/ieee80211_wx.c               |   26 +++++++++++++++-----------
 net/mac80211/ieee80211_i.h                 |    5 ++++-
 net/mac80211/ieee80211_ioctl.c             |    2 +-
 net/mac80211/ieee80211_sta.c               |   23 +++++++++++++----------
 16 files changed, 136 insertions(+), 75 deletions(-)

diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 074055e..ede5e59 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -7234,6 +7234,7 @@ out:
  * format that the Wireless Tools will understand - Jean II
  */
 static inline char *airo_translate_scan(struct net_device *dev,
+					struct iw_request_info *info,
 					char *current_ev,
 					char *end_buf,
 					BSSListRid *bss)
@@ -7259,7 +7260,8 @@ static inline char *airo_translate_scan(struct net_device *dev,
 		iwe.u.data.length = 32;
 	iwe.cmd = SIOCGIWESSID;
 	iwe.u.data.flags = 1;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid);
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid,
+					  info);
 
 	/* Add mode */
 	iwe.cmd = SIOCGIWMODE;
@@ -7307,7 +7309,8 @@ static inline char *airo_translate_scan(struct net_device *dev,
 	else
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	iwe.u.data.length = 0;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid);
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid,
+					  info);
 
 	/* Rate : stuffing multiple values in a single event require a bit
 	 * more of magic - Jean II */
@@ -7336,7 +7339,8 @@ static inline char *airo_translate_scan(struct net_device *dev,
 		iwe.cmd = IWEVCUSTOM;
 		sprintf(buf, "bcn_int=%d", bss->beaconInterval);
 		iwe.u.data.length = strlen(buf);
-		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+						  buf, info);
 		kfree(buf);
 	}
 
@@ -7371,7 +7375,7 @@ static inline char *airo_translate_scan(struct net_device *dev,
 					iwe.u.data.length = min(info_element->len + 2,
 								  MAX_WPA_IE_LEN);
 					current_ev = iwe_stream_add_point(current_ev, end_buf,
-							&iwe, (char *) info_element);
+									  &iwe, (char *) info_element, info);
 				}
 				break;
 
@@ -7380,7 +7384,7 @@ static inline char *airo_translate_scan(struct net_device *dev,
 				iwe.u.data.length = min(info_element->len + 2,
 							  MAX_WPA_IE_LEN);
 				current_ev = iwe_stream_add_point(current_ev, end_buf,
-						&iwe, (char *) info_element);
+								  &iwe, (char *) info_element, info);
 				break;
 
 			default:
@@ -7419,7 +7423,7 @@ static int airo_get_scan(struct net_device *dev,
 
 	list_for_each_entry (net, &ai->network_list, list) {
 		/* Translate to WE format this entry */
-		current_ev = airo_translate_scan(dev, current_ev,
+		current_ev = airo_translate_scan(dev, info, current_ev,
 						 extra + dwrq->length,
 						 &net->bss);
 
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 059ce3f..03ab953 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -2327,7 +2327,7 @@ static int atmel_get_scan(struct net_device *dev,
 			iwe.u.data.length = 32;
 		iwe.cmd = SIOCGIWESSID;
 		iwe.u.data.flags = 1;
-		current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, priv->BSSinfo[i].SSID);
+		current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, priv->BSSinfo[i].SSID, info);
 
 		iwe.cmd = SIOCGIWMODE;
 		iwe.u.mode = priv->BSSinfo[i].BSStype;
@@ -2352,7 +2352,7 @@ static int atmel_get_scan(struct net_device *dev,
 		else
 			iwe.u.data.flags = IW_ENCODE_DISABLED;
 		iwe.u.data.length = 0;
-		current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, NULL);
+		current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, NULL, info);
 	}
 
 	/* Length of data */
diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h
index 547ba84..3a386a6 100644
--- a/drivers/net/wireless/hostap/hostap.h
+++ b/drivers/net/wireless/hostap/hostap.h
@@ -67,7 +67,8 @@ void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
 int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
 			   struct iw_quality qual[], int buf_size,
 			   int aplist);
-int prism2_ap_translate_scan(struct net_device *dev, char *buffer);
+int prism2_ap_translate_scan(struct net_device *dev,
+			     struct iw_request_info *info, char *buffer);
 int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param);
 
 
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
index 6bbdb76..8090e87 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -2381,7 +2381,8 @@ int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
 
 /* Translate our list of Access Points & Stations to a card independant
  * format that the Wireless Tools will understand - Jean II */
-int prism2_ap_translate_scan(struct net_device *dev, char *buffer)
+int prism2_ap_translate_scan(struct net_device *dev,
+			     struct iw_request_info *info, char *buffer)
 {
 	struct hostap_interface *iface;
 	local_info_t *local;
@@ -2449,7 +2450,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer)
 			iwe.u.data.flags = 1;
 			current_ev = iwe_stream_add_point(current_ev, end_buf,
 							  &iwe,
-							  sta->u.ap.ssid);
+							  sta->u.ap.ssid,
+							  info);
 
 			memset(&iwe, 0, sizeof(iwe));
 			iwe.cmd = SIOCGIWENCODE;
@@ -2460,8 +2462,9 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer)
 				iwe.u.data.flags = IW_ENCODE_DISABLED;
 			current_ev = iwe_stream_add_point(current_ev, end_buf,
 							  &iwe,
-							  sta->u.ap.ssid
-							  /* 0 byte memcpy */);
+							  sta->u.ap.ssid,
+							  /* 0 byte memcpy */
+							  info);
 
 			if (sta->u.ap.channel > 0 &&
 			    sta->u.ap.channel <= FREQ_COUNT) {
@@ -2481,7 +2484,7 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer)
 				sta->listen_interval);
 			iwe.u.data.length = strlen(buf);
 			current_ev = iwe_stream_add_point(current_ev, end_buf,
-							  &iwe, buf);
+							  &iwe, buf, info);
 		}
 #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
 
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
index d8f5efc..1c18a39 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/hostap/hostap_ioctl.c
@@ -1794,6 +1794,7 @@ static int prism2_ioctl_siwscan(struct net_device *dev,
 
 #ifndef PRISM2_NO_STATION_MODES
 static char * __prism2_translate_scan(local_info_t *local,
+				      struct iw_request_info *info,
 				      struct hfa384x_hostscan_result *scan,
 				      struct hostap_bss_info *bss,
 				      char *current_ev, char *end_buf)
@@ -1833,7 +1834,8 @@ static char * __prism2_translate_scan(local_info_t *local,
 	iwe.cmd = SIOCGIWESSID;
 	iwe.u.data.length = ssid_len;
 	iwe.u.data.flags = 1;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid);
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid,
+					  info);
 
 	memset(&iwe, 0, sizeof(iwe));
 	iwe.cmd = SIOCGIWMODE;
@@ -1896,7 +1898,7 @@ static char * __prism2_translate_scan(local_info_t *local,
 	else
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	iwe.u.data.length = 0;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "", info);
 
 	/* TODO: add SuppRates into BSS table */
 	if (scan) {
@@ -1926,14 +1928,14 @@ static char * __prism2_translate_scan(local_info_t *local,
 		sprintf(buf, "bcn_int=%d", le16_to_cpu(scan->beacon_interval));
 		iwe.u.data.length = strlen(buf);
 		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-						  buf);
+						  buf, info);
 
 		memset(&iwe, 0, sizeof(iwe));
 		iwe.cmd = IWEVCUSTOM;
 		sprintf(buf, "resp_rate=%d", le16_to_cpu(scan->rate));
 		iwe.u.data.length = strlen(buf);
 		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-						  buf);
+						  buf, info);
 
 		if (local->last_scan_type == PRISM2_HOSTSCAN &&
 		    (capabilities & WLAN_CAPABILITY_IBSS)) {
@@ -1942,7 +1944,7 @@ static char * __prism2_translate_scan(local_info_t *local,
 			sprintf(buf, "atim=%d", le16_to_cpu(scan->atim));
 			iwe.u.data.length = strlen(buf);
 			current_ev = iwe_stream_add_point(current_ev, end_buf,
-							  &iwe, buf);
+							  &iwe, buf, info);
 		}
 	}
 	kfree(buf);
@@ -1952,7 +1954,7 @@ static char * __prism2_translate_scan(local_info_t *local,
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = bss->wpa_ie_len;
 		current_ev = iwe_stream_add_point(
-			current_ev, end_buf, &iwe, bss->wpa_ie);
+			current_ev, end_buf, &iwe, bss->wpa_ie, info);
 	}
 
 	if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) {
@@ -1960,7 +1962,7 @@ static char * __prism2_translate_scan(local_info_t *local,
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = bss->rsn_ie_len;
 		current_ev = iwe_stream_add_point(
-			current_ev, end_buf, &iwe, bss->rsn_ie);
+			current_ev, end_buf, &iwe, bss->rsn_ie, info);
 	}
 
 	return current_ev;
@@ -1970,6 +1972,7 @@ static char * __prism2_translate_scan(local_info_t *local,
 /* Translate scan data returned from the card to a card independant
  * format that the Wireless Tools will understand - Jean II */
 static inline int prism2_translate_scan(local_info_t *local,
+					struct iw_request_info *info,
 					char *buffer, int buflen)
 {
 	struct hfa384x_hostscan_result *scan;
@@ -2000,13 +2003,14 @@ static inline int prism2_translate_scan(local_info_t *local,
 			if (memcmp(bss->bssid, scan->bssid, ETH_ALEN) == 0) {
 				bss->included = 1;
 				current_ev = __prism2_translate_scan(
-					local, scan, bss, current_ev, end_buf);
+					local, info, scan, bss, current_ev,
+					end_buf);
 				found++;
 			}
 		}
 		if (!found) {
 			current_ev = __prism2_translate_scan(
-				local, scan, NULL, current_ev, end_buf);
+				local, info, scan, NULL, current_ev, end_buf);
 		}
 		/* Check if there is space for one more entry */
 		if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) {
@@ -2024,7 +2028,7 @@ static inline int prism2_translate_scan(local_info_t *local,
 		bss = list_entry(ptr, struct hostap_bss_info, list);
 		if (bss->included)
 			continue;
-		current_ev = __prism2_translate_scan(local, NULL, bss,
+		current_ev = __prism2_translate_scan(local, info, NULL, bss,
 						     current_ev, end_buf);
 		/* Check if there is space for one more entry */
 		if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) {
@@ -2071,7 +2075,7 @@ static inline int prism2_ioctl_giwscan_sta(struct net_device *dev,
 	}
 	local->scan_timestamp = 0;
 
-	res = prism2_translate_scan(local, extra, data->length);
+	res = prism2_translate_scan(local, info, extra, data->length);
 
 	if (res >= 0) {
 		data->length = res;
@@ -2104,7 +2108,7 @@ static int prism2_ioctl_giwscan(struct net_device *dev,
 		 * Jean II */
 
 		/* Translate to WE format */
-		res = prism2_ap_translate_scan(dev, extra);
+		res = prism2_ap_translate_scan(dev, info, extra);
 		if (res >= 0) {
 			printk(KERN_DEBUG "Scan result translation succeeded "
 			       "(length=%d)\n", res);
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
index ad1e67d..214bc96 100644
--- a/drivers/net/wireless/libertas/scan.c
+++ b/drivers/net/wireless/libertas/scan.c
@@ -1444,8 +1444,9 @@ out:
 #define MAX_CUSTOM_LEN 64
 
 static inline char *libertas_translate_scan(wlan_private *priv,
-					char *start, char *stop,
-					struct bss_descriptor *bss)
+					    struct iw_request_info *info,
+					    char *start, char *stop,
+					    struct bss_descriptor *bss)
 {
 	wlan_adapter *adapter = priv->adapter;
 	struct chan_freq_power *cfp;
@@ -1476,7 +1477,7 @@ static inline char *libertas_translate_scan(wlan_private *priv,
 	iwe.cmd = SIOCGIWESSID;
 	iwe.u.data.flags = 1;
 	iwe.u.data.length = min((u32) bss->ssid_len, (u32) IW_ESSID_MAX_SIZE);
-	start = iwe_stream_add_point(start, stop, &iwe, bss->ssid);
+	start = iwe_stream_add_point(start, stop, &iwe, bss->ssid, info);
 
 	/* Mode */
 	iwe.cmd = SIOCGIWMODE;
@@ -1533,7 +1534,7 @@ static inline char *libertas_translate_scan(wlan_private *priv,
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	}
 	iwe.u.data.length = 0;
-	start = iwe_stream_add_point(start, stop, &iwe, bss->ssid);
+	start = iwe_stream_add_point(start, stop, &iwe, bss->ssid, info);
 
 	current_val = start + IW_EV_LCP_LEN;
 
@@ -1567,7 +1568,7 @@ static inline char *libertas_translate_scan(wlan_private *priv,
 		memcpy(buf, bss->wpa_ie, bss->wpa_ie_len);
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = bss->wpa_ie_len;
-		start = iwe_stream_add_point(start, stop, &iwe, buf);
+		start = iwe_stream_add_point(start, stop, &iwe, buf, info);
 	}
 
 	memset(&iwe, 0, sizeof(iwe));
@@ -1576,7 +1577,7 @@ static inline char *libertas_translate_scan(wlan_private *priv,
 		memcpy(buf, bss->rsn_ie, bss->rsn_ie_len);
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = bss->rsn_ie_len;
-		start = iwe_stream_add_point(start, stop, &iwe, buf);
+		start = iwe_stream_add_point(start, stop, &iwe, buf, info);
 	}
 
 	if (bss->mesh) {
@@ -1588,7 +1589,8 @@ static inline char *libertas_translate_scan(wlan_private *priv,
 		              "mesh-type: olpc");
 		iwe.u.data.length = p - custom;
 		if (iwe.u.data.length)
-			start = iwe_stream_add_point(start, stop, &iwe, custom);
+			start = iwe_stream_add_point(start, stop, &iwe, custom,
+						     info);
 	}
 
 out:
@@ -1650,7 +1652,8 @@ int libertas_get_scan(struct net_device *dev, struct iw_request_info *info,
 		}
 
 		/* Translate to WE format this entry */
-		next_ev = libertas_translate_scan(priv, ev, stop, iter_bss);
+		next_ev = libertas_translate_scan(priv, info, ev, stop,
+						  iter_bss);
 		if (next_ev == NULL)
 			continue;
 		ev = next_ev;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index ca6c2da..a5d8ab4 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -3909,6 +3909,7 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
  * format that the Wireless Tools will understand - Jean II
  * Return message length or -errno for fatal errors */
 static inline int orinoco_translate_scan(struct net_device *dev,
+					 struct iw_request_info *info,
 					 char *buffer,
 					 char *scan,
 					 int scan_len)
@@ -3989,7 +3990,7 @@ static inline int orinoco_translate_scan(struct net_device *dev,
 			iwe.u.data.length = 32;
 		iwe.cmd = SIOCGIWESSID;
 		iwe.u.data.flags = 1;
-		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid);
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid, info);
 
 		/* Add mode */
 		iwe.cmd = SIOCGIWMODE;
@@ -4032,7 +4033,7 @@ static inline int orinoco_translate_scan(struct net_device *dev,
 		else
 			iwe.u.data.flags = IW_ENCODE_DISABLED;
 		iwe.u.data.length = 0;
-		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid);
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid, info);
 
 		/* Bit rate is not available in Lucent/Agere firmwares */
 		if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
@@ -4102,7 +4103,7 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
 		/* We have some results to push back to user space */
 
 		/* Translate to WE format */
-		int ret = orinoco_translate_scan(dev, extra,
+		int ret = orinoco_translate_scan(dev, info, extra,
 						 priv->scan_result,
 						 priv->scan_len);
 
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index 6d80ca4..3261bf4 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -572,8 +572,9 @@ prism54_set_scan(struct net_device *dev, struct iw_request_info *info,
  */
 
 static char *
-prism54_translate_bss(struct net_device *ndev, char *current_ev,
-		      char *end_buf, struct obj_bss *bss, char noise)
+prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info,
+		      char *current_ev, char *end_buf, struct obj_bss *bss,
+		      char noise)
 {
 	struct iw_event iwe;	/* Temporary buffer */
 	short cap;
@@ -595,7 +596,7 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev,
 	iwe.u.data.flags = 1;
 	iwe.cmd = SIOCGIWESSID;
 	current_ev = iwe_stream_add_point(current_ev, end_buf,
-					  &iwe, bss->ssid.octets);
+					  &iwe, bss->ssid.octets, info);
 
 	/* Capabilities */
 #define CAP_ESS 0x01
@@ -622,7 +623,8 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev,
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	iwe.u.data.length = 0;
 	iwe.cmd = SIOCGIWENCODE;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL);
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL,
+					  info);
 
 	/* Add frequency. (short) bss->channel is the frequency in MHz */
 	iwe.u.freq.m = bss->channel;
@@ -646,7 +648,7 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev,
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = min(wpa_ie_len, (size_t)MAX_WPA_IE_LEN);
 		current_ev = iwe_stream_add_point(current_ev, end_buf,
-				&iwe, wpa_ie);
+						  &iwe, wpa_ie, info);
 	}
 	/* Do the bitrates */
 	{
@@ -711,7 +713,7 @@ prism54_get_scan(struct net_device *ndev, struct iw_request_info *info,
 
 	/* ok now, scan the list and translate its info */
 	for (i = 0; i < (int) bsslist->nr; i++) {
-		current_ev = prism54_translate_bss(ndev, current_ev,
+		current_ev = prism54_translate_bss(ndev, info, current_ev,
 						   extra + dwrq->length,
 						   &(bsslist->bsslist[i]),
 						   noise);
@@ -2705,6 +2707,7 @@ prism2_ioctl_scan_req(struct net_device *ndev,
                      struct prism2_hostapd_param *param)
 {
 	islpci_private *priv = netdev_priv(ndev);
+	struct iw_request_info info;
 	int i, rvalue;
 	struct obj_bsslist *bsslist;
 	u32 noise = 0;
@@ -2728,9 +2731,12 @@ prism2_ioctl_scan_req(struct net_device *ndev,
 	rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r);
 	bsslist = r.ptr;
 
+	info.cmd = PRISM54_HOSTAPD;
+	info.flags = 0;
+
 	/* ok now, scan the list and translate its info */
 	for (i = 0; i < min(IW_MAX_AP, (int) bsslist->nr); i++)
-		current_ev = prism54_translate_bss(ndev, current_ev,
+		current_ev = prism54_translate_bss(ndev, current_ev, &info,
 						   extra + IW_SCAN_MAX_DATA,
 						   &(bsslist->bsslist[i]),
 						   noise);
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index 42a36b3..9dbf028 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -1633,7 +1633,8 @@ static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info,
 		current_ev = iwe_stream_add_point(current_ev,
 						  extra + IW_SCAN_MAX_DATA,
 						  &iwe,
-						  this->bss_set[i].ssid.essid);
+						  this->bss_set[i].ssid.essid,
+						  info);
 		iwe.cmd	   = SIOCGIWMODE;
 		iwe.u.mode = this->bss_set[i].bss_type;
 		current_ev = iwe_stream_add_event(current_ev,
@@ -1653,7 +1654,8 @@ static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info,
 		iwe.u.data.length = 0;
 		current_ev = iwe_stream_add_point(current_ev,
 						  extra + IW_SCAN_MAX_DATA,
-						  &iwe, NULL);
+						  &iwe, NULL,
+						  info);
 	}
 	/* Length of data */
 	wrqu->data.length = (current_ev - extra);
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index d5c0c66..4fd83fd 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -1157,7 +1157,8 @@ static int zd1201_get_scan(struct net_device *dev,
 		iwe.cmd = SIOCGIWESSID;
 		iwe.u.data.length = zd->rxdata[i+16];
 		iwe.u.data.flags = 1;
-		cev = iwe_stream_add_point(cev, end_buf, &iwe, zd->rxdata+i+18);
+		cev = iwe_stream_add_point(cev, end_buf, &iwe, zd->rxdata+i+18,
+					   info);
 
 		iwe.cmd = SIOCGIWMODE;
 		if (zd->rxdata[i+14]&0x01)
@@ -1186,7 +1187,7 @@ static int zd1201_get_scan(struct net_device *dev,
 			iwe.u.data.flags = IW_ENCODE_ENABLED;
 		else
 			iwe.u.data.flags = IW_ENCODE_DISABLED;
-		cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL);
+		cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL, info);
 		
 		iwe.cmd = IWEVQUAL;
 		iwe.u.qual.qual = zd->rxdata[i+4];
diff --git a/include/linux/wireless.h b/include/linux/wireless.h
index 2088524..ff25e06 100644
--- a/include/linux/wireless.h
+++ b/include/linux/wireless.h
@@ -1098,6 +1098,12 @@ struct iw_event
 #define IW_EV_POINT_LEN	(IW_EV_LCP_LEN + sizeof(struct iw_point) - \
 			 IW_EV_POINT_OFF)
 
+#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
+#define IW_EV_COMPAT_POINT_LEN	\
+	(IW_EV_LCP_LEN + sizeof(struct compat_iw_point) - \
+	 IW_EV_POINT_OFF)
+#endif
+
 /* Size of the Event prefix when packed in stream */
 #define IW_EV_LCP_PK_LEN	(4)
 /* Size of the various events when packed in stream */
diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h
index c99a8ee..5530e09 100644
--- a/include/net/iw_handler.h
+++ b/include/net/iw_handler.h
@@ -510,9 +510,19 @@ static inline char *
 iwe_stream_add_point(char *	stream,		/* Stream of events */
 		     char *	ends,		/* End of stream */
 		     struct iw_event *iwe,	/* Payload length + flags */
-		     char *	extra)		/* More payload */
+		     char *	extra,		/* More payload */
+		     struct iw_request_info *info)
 {
 	int	event_len = IW_EV_POINT_LEN + iwe->u.data.length;
+	int	point_len = IW_EV_POINT_LEN;
+
+#ifdef CONFIG_COMPAT
+	if (info->flags & IW_REQUEST_FLAG_COMPAT) {
+		event_len = IW_EV_COMPAT_POINT_LEN + iwe->u.data.length;
+		point_len = IW_EV_COMPAT_POINT_LEN;
+	}
+#endif
+
 	/* Check if it's possible */
 	if(likely((stream + event_len) < ends)) {
 		iwe->len = event_len;
@@ -520,7 +530,7 @@ iwe_stream_add_point(char *	stream,		/* Stream of events */
 		memcpy(stream + IW_EV_LCP_LEN,
 		       ((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
 		       IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-		memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length);
+		memcpy(stream + point_len, extra, iwe->u.data.length);
 		stream += event_len;
 	}
 	return stream;
@@ -591,9 +601,19 @@ iwe_stream_check_add_point(char *	stream,		/* Stream of events */
 			   char *	ends,		/* End of stream */
 			   struct iw_event *iwe,	/* Payload length + flags */
 			   char *	extra,		/* More payload */
-			   int *	perr)		/* Error report */
+			   int *	perr,		/* Error report */
+			   struct iw_request_info *info)
 {
 	int	event_len = IW_EV_POINT_LEN + iwe->u.data.length;
+	int	point_len = IW_EV_POINT_LEN;
+
+#ifdef CONFIG_COMPAT
+	if (info->flags & IW_REQUEST_FLAG_COMPAT) {
+		event_len = IW_EV_COMPAT_POINT_LEN + iwe->u.data.length;
+		point_len = IW_EV_COMPAT_POINT_LEN;
+	}
+#endif
+
 	/* Check if it's possible */
 	if(likely((stream + event_len) < ends)) {
 		iwe->len = event_len;
@@ -601,7 +621,7 @@ iwe_stream_check_add_point(char *	stream,		/* Stream of events */
 		memcpy(stream + IW_EV_LCP_LEN,
 		       ((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
 		       IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-		memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length);
+		memcpy(stream + point_len, extra, iwe->u.data.length);
 		stream += event_len;
 	} else
 		*perr = -E2BIG;
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
index d309e8f..fc0777c 100644
--- a/net/ieee80211/ieee80211_wx.c
+++ b/net/ieee80211/ieee80211_wx.c
@@ -43,8 +43,9 @@ static const char *ieee80211_modes[] = {
 
 #define MAX_CUSTOM_LEN 64
 static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
-					   char *start, char *stop,
-					   struct ieee80211_network *network)
+				      char *start, char *stop,
+				      struct ieee80211_network *network,
+				      struct iw_request_info *info)
 {
 	char custom[MAX_CUSTOM_LEN];
 	char *p;
@@ -66,10 +67,12 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
 	iwe.u.data.flags = 1;
 	if (network->flags & NETWORK_EMPTY_ESSID) {
 		iwe.u.data.length = sizeof("<hidden>");
-		start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
+		start = iwe_stream_add_point(start, stop, &iwe, "<hidden>",
+					     info);
 	} else {
 		iwe.u.data.length = min(network->ssid_len, (u8) 32);
-		start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
+		start = iwe_stream_add_point(start, stop, &iwe, network->ssid,
+					     info);
 	}
 
 	/* Add the protocol name */
@@ -104,7 +107,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
 	else
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	iwe.u.data.length = 0;
-	start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
+	start = iwe_stream_add_point(start, stop, &iwe, network->ssid, info);
 
 	/* Add basic and extended rates */
 	/* Rate : stuffing multiple values in a single event require a bit
@@ -188,7 +191,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
 
 	iwe.u.data.length = p - custom;
 	if (iwe.u.data.length)
-		start = iwe_stream_add_point(start, stop, &iwe, custom);
+		start = iwe_stream_add_point(start, stop, &iwe, custom, info);
 
 	memset(&iwe, 0, sizeof(iwe));
 	if (network->wpa_ie_len) {
@@ -196,7 +199,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
 		memcpy(buf, network->wpa_ie, network->wpa_ie_len);
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = network->wpa_ie_len;
-		start = iwe_stream_add_point(start, stop, &iwe, buf);
+		start = iwe_stream_add_point(start, stop, &iwe, buf, info);
 	}
 
 	memset(&iwe, 0, sizeof(iwe));
@@ -205,7 +208,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
 		memcpy(buf, network->rsn_ie, network->rsn_ie_len);
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = network->rsn_ie_len;
-		start = iwe_stream_add_point(start, stop, &iwe, buf);
+		start = iwe_stream_add_point(start, stop, &iwe, buf, info);
 	}
 
 	/* Add EXTRA: Age to display seconds since last beacon/probe response
@@ -217,7 +220,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
 		      jiffies_to_msecs(jiffies - network->last_scanned));
 	iwe.u.data.length = p - custom;
 	if (iwe.u.data.length)
-		start = iwe_stream_add_point(start, stop, &iwe, custom);
+		start = iwe_stream_add_point(start, stop, &iwe, custom, info);
 
 	/* Add spectrum management information */
 	iwe.cmd = -1;
@@ -238,7 +241,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
 
 	if (iwe.cmd == IWEVCUSTOM) {
 		iwe.u.data.length = p - custom;
-		start = iwe_stream_add_point(start, stop, &iwe, custom);
+		start = iwe_stream_add_point(start, stop, &iwe, custom, info);
 	}
 
 	return start;
@@ -272,7 +275,8 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
 
 		if (ieee->scan_age == 0 ||
 		    time_after(network->last_scanned + ieee->scan_age, jiffies))
-			ev = ieee80211_translate_scan(ieee, ev, stop, network);
+			ev = ieee80211_translate_scan(ieee, ev, stop, network,
+						      info);
 		else
 			IEEE80211_DEBUG_SCAN("Not showing network '%s ("
 					     "%s)' due to age (%dms).\n",
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 72e1c93..b8306aa 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -23,6 +23,7 @@
 #include <linux/spinlock.h>
 #include <linux/etherdevice.h>
 #include <net/wireless.h>
+#include <net/iw_handler.h>
 #include "ieee80211_key.h"
 #include "sta_info.h"
 
@@ -748,7 +749,9 @@ int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid);
 int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len);
 void ieee80211_sta_req_auth(struct net_device *dev,
 			    struct ieee80211_if_sta *ifsta);
-int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len);
+int ieee80211_sta_scan_results(struct net_device *dev,
+			       struct iw_request_info *info,
+			       char *buf, size_t len);
 void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
 			   struct ieee80211_rx_status *rx_status);
 void ieee80211_rx_bss_list_init(struct net_device *dev);
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index 7027eed..0f686f1 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -560,7 +560,7 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev,
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	if (local->sta_scanning)
 		return -EAGAIN;
-	res = ieee80211_sta_scan_results(dev, extra, data->length);
+	res = ieee80211_sta_scan_results(dev, info, extra, data->length);
 	if (res >= 0) {
 		data->length = res;
 		return 0;
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 16afd24..29a059e 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -2885,6 +2885,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
 
 static char *
 ieee80211_sta_scan_result(struct net_device *dev,
+			  struct iw_request_info *info,
 			  struct ieee80211_sta_bss *bss,
 			  char *current_ev, char *end_buf)
 {
@@ -2919,7 +2920,7 @@ ieee80211_sta_scan_result(struct net_device *dev,
 	iwe.u.data.length = bss->ssid_len;
 	iwe.u.data.flags = 1;
 	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-					  bss->ssid);
+					  bss->ssid, info);
 
 	if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
 		memset(&iwe, 0, sizeof(iwe));
@@ -2959,14 +2960,14 @@ ieee80211_sta_scan_result(struct net_device *dev,
 	else
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	iwe.u.data.length = 0;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "", info);
 
 	if (bss && bss->wpa_ie) {
 		memset(&iwe, 0, sizeof(iwe));
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = bss->wpa_ie_len;
 		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-						  bss->wpa_ie);
+						  bss->wpa_ie, info);
 	}
 
 	if (bss && bss->rsn_ie) {
@@ -2974,7 +2975,7 @@ ieee80211_sta_scan_result(struct net_device *dev,
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = bss->rsn_ie_len;
 		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-						  bss->rsn_ie);
+						  bss->rsn_ie, info);
 	}
 
 	if (bss && bss->supp_rates_len > 0) {
@@ -3005,7 +3006,7 @@ ieee80211_sta_scan_result(struct net_device *dev,
 			sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp));
 			iwe.u.data.length = strlen(buf);
 			current_ev = iwe_stream_add_point(current_ev, end_buf,
-							  &iwe, buf);
+							  &iwe, buf, info);
 			kfree(buf);
 		}
 	}
@@ -3025,14 +3026,14 @@ ieee80211_sta_scan_result(struct net_device *dev,
 		sprintf(buf, "bcn_int=%d", bss->beacon_int);
 		iwe.u.data.length = strlen(buf);
 		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-						  buf);
+						  buf, info);
 
 		memset(&iwe, 0, sizeof(iwe));
 		iwe.cmd = IWEVCUSTOM;
 		sprintf(buf, "capab=0x%04x", bss->capability);
 		iwe.u.data.length = strlen(buf);
 		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-						  buf);
+						  buf, info);
 
 		kfree(buf);
 		break;
@@ -3042,7 +3043,9 @@ ieee80211_sta_scan_result(struct net_device *dev,
 }
 
 
-int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len)
+int ieee80211_sta_scan_results(struct net_device *dev,
+			       struct iw_request_info *info,
+			       char *buf, size_t len)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	char *current_ev = buf;
@@ -3055,8 +3058,8 @@ int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len)
 			spin_unlock_bh(&local->sta_bss_lock);
 			return -E2BIG;
 		}
-		current_ev = ieee80211_sta_scan_result(dev, bss, current_ev,
-						       end_buf);
+		current_ev = ieee80211_sta_scan_result(dev, info, bss,
+						       current_ev, end_buf);
 	}
 	spin_unlock_bh(&local->sta_bss_lock);
 	return current_ev - buf;
-- 
1.5.4.rc1

^ permalink raw reply related

* hard_start_xmit struct sk_buff read-only?
From: Matti Linnanvuori @ 2007-12-22  6:32 UTC (permalink / raw)
  To: netdev

An old article about Linux's network drivers mentioned
that the struct sk_buff whose pointer is passed to a
driver's hard_start_xmit function is read-only to the
function. Is that still so? If it is, there is no
mention about it in the kernel tree as far as I know.


      Heute schon einen Blick in die Zukunft von E-Mails wagen? www.yahoo.de/mail

^ permalink raw reply

* Re: [ETH]: Combine format_addr() with print_mac().
From: Joe Perches @ 2007-12-22  7:02 UTC (permalink / raw)
  To: Michael Chan; +Cc: David Miller, netdev, anilgv, michaelc, david.somayajulu
In-Reply-To: <1198295882.6752.9.camel@dell>

On Fri, 2007-12-21 at 19:58 -0800, Michael Chan wrote:
> The dev_addr is declared as unsigned char* in struct net_device.  To be
> consistent, can we change print_mac() and MAC_BUF to use unsigned char*?
> They are really the same.

That's fine by me.  I like consistency.
I don't remember why it was u8 and not unsigned char.

> > ssize_t? shouldn't it be size_t?
> I'm just keeping the prototype unchanged as originally defined in net-
> sysfs.c

It's painless to change the prototype.
size_t seems more sensible.

> > Indexing buf by int len is unchecked.
> > That could lead to unintended buffer overruns.
> > Maybe add a buflen argument and use snprintf?
> Again, I kept the semantics the same as the original, but will be happy
> to add a buflen for better checking.

That sounds good.

cheers, Joe


^ permalink raw reply

* Re: [WEXT 3/12]: Extract standard call iw_point handling into seperate function.
From: Joe Perches @ 2007-12-22  7:22 UTC (permalink / raw)
  To: David Miller
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20071221.205407.165162566.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>

On Fri, 2007-12-21 at 20:54 -0800, David Miller wrote:
> [WEXT]: Extract standard call iw_point handling into seperate function.

This potentially does copy_from_user twice,
first to the stack, second to a kmalloc'ed area.
Perhaps it's better to kmalloc first and copy once?

^ permalink raw reply

* Re: [PATCH] MAINTAINERS: remove Adam Fritzler, update his email address in other sources
From: Andrew Morton @ 2007-12-22  8:44 UTC (permalink / raw)
  To: Joe Perches
  Cc: linux-kernel, Adam Fritzler, James E.J. Bottomley, Jeff Garzik,
	Mike Phillips, Philip Blundell, linux-scsi, netdev, Adrian Bunk
In-Reply-To: <1197953283.4891.24.camel@localhost>

On Mon, 17 Dec 2007 20:48:03 -0800 Joe Perches <joe@perches.com> wrote:

> Back to Adam Fritzler...
> 
> ...
> 
> diff --git a/CREDITS b/CREDITS
> index ee909f2..449ec7f 100644
> --- a/CREDITS
> +++ b/CREDITS
> @@ -1124,6 +1124,9 @@ S: 1150 Ringwood Court
>  S: San Jose, California 95131
>  S: USA
>  
> +N: Adam Fritzler
> +E: mid@zigamorph.net
> +
>  N: Fernando Fuganti
>  E: fuganti@conectiva.com.br
>  E: fuganti@netbank.com.br
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9507b42..690f172 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3758,13 +3758,6 @@ W:	http://www.kernel.org/pub/linux/kernel/people/bunk/trivial/
>  T:	git kernel.org:/pub/scm/linux/kernel/git/bunk/trivial.git
>  S:	Maintained
>  
> -TMS380 TOKEN-RING NETWORK DRIVER
> -P:	Adam Fritzler
> -M:	mid@auk.cx
> -L:	linux-tr@linuxtr.net
> -W:	http://www.auk.cx/tms380tr/
> -S:	Maintained

What was the rationale for removing Adam from MAINTAINERS?

That should have been in the non-existent changelog.  Please always reissue
a complete changelog when resending any patch.

hm, linux-tr.net seems to be defunct.  So I guess that orphaning TMS380 is
appropriate, if Adam has left us.  Has he?


^ permalink raw reply

* Re: hard_start_xmit struct sk_buff read-only?
From: Jeff Garzik @ 2007-12-22  8:50 UTC (permalink / raw)
  To: Matti Linnanvuori; +Cc: netdev
In-Reply-To: <249190.1925.qm@web52006.mail.re2.yahoo.com>

Matti Linnanvuori wrote:
> An old article about Linux's network drivers mentioned
> that the struct sk_buff whose pointer is passed to a
> driver's hard_start_xmit function is read-only to the
> function. Is that still so? If it is, there is no
> mention about it in the kernel tree as far as I know.

This begs the question, what are you trying to do?  ;-)

In terms of object lifetimes, control passes to the net driver when 
->hard_start_xmit() is called, but that does not mean you can freely 
scribble over things -- the skb may have been cloned, its destructor 
callback still needs to be called (via dev_kfree_skb), etc.

	Jeff




^ permalink raw reply

* Re: [PATCH] MAINTAINERS: remove Adam Fritzler, update his email address in other sources
From: Jeff Garzik @ 2007-12-22  8:53 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Joe Perches, linux-kernel, Adam Fritzler, James E.J. Bottomley,
	Mike Phillips, Philip Blundell, linux-scsi, netdev, Adrian Bunk
In-Reply-To: <20071222004448.36c95d98.akpm@linux-foundation.org>

Andrew Morton wrote:
> On Mon, 17 Dec 2007 20:48:03 -0800 Joe Perches <joe@perches.com> wrote:
> 
>> Back to Adam Fritzler...
>>
>> ...
>>
>> diff --git a/CREDITS b/CREDITS
>> index ee909f2..449ec7f 100644
>> --- a/CREDITS
>> +++ b/CREDITS
>> @@ -1124,6 +1124,9 @@ S: 1150 Ringwood Court
>>  S: San Jose, California 95131
>>  S: USA
>>  
>> +N: Adam Fritzler
>> +E: mid@zigamorph.net
>> +
>>  N: Fernando Fuganti
>>  E: fuganti@conectiva.com.br
>>  E: fuganti@netbank.com.br
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 9507b42..690f172 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -3758,13 +3758,6 @@ W:	http://www.kernel.org/pub/linux/kernel/people/bunk/trivial/
>>  T:	git kernel.org:/pub/scm/linux/kernel/git/bunk/trivial.git
>>  S:	Maintained
>>  
>> -TMS380 TOKEN-RING NETWORK DRIVER
>> -P:	Adam Fritzler
>> -M:	mid@auk.cx
>> -L:	linux-tr@linuxtr.net
>> -W:	http://www.auk.cx/tms380tr/
>> -S:	Maintained
> 
> What was the rationale for removing Adam from MAINTAINERS?
> 
> That should have been in the non-existent changelog.  Please always reissue
> a complete changelog when resending any patch.
> 
> hm, linux-tr.net seems to be defunct.  So I guess that orphaning TMS380 is
> appropriate, if Adam has left us.  Has he?

I don't know that specific answer, but in terms of general policy...

I wouldn't apply this patch until they have been in 
Documentation/people-removal-schedule.txt for a year or so ;-)  More 
seriously, its not like MAINTAINERS is a mission critical correctness 
target -- so I think we can do a better job than just yanking people 
from the file like this.

	Jeff




^ permalink raw reply

* Re: [PATCH] MAINTAINERS: remove Adam Fritzler, update his email address in other sources
From: Joe Perches @ 2007-12-22  8:59 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Andrew Morton, linux-kernel, Adam Fritzler, James E.J. Bottomley,
	Mike Phillips, Philip Blundell, linux-scsi, netdev, Adrian Bunk
In-Reply-To: <476CD06F.3090402@pobox.com>

On Sat, 2007-12-22 at 03:53 -0500, Jeff Garzik wrote:
> > What was the rationale for removing Adam from MAINTAINERS?

I sent him a patch to that address and it bounced.
I sent him a note asking if he was still a maintainer to
his new address and he replied:

http://www.gossamer-threads.com/lists/linux/kernel/855361

On Mon, Dec 17, 2007 at 01:03:48PM -0800, Joe Perches wrote: 
> > You seem to have an old email address in the 
> > linux-kernel MAINTAINERS file. 
> > Should it be deleted or changed? 
On Mon, 2007-12-17 at 19:27 -0800, Adam Fritzler wrote: 
> I am no longer actively involved. If you can mark me as a former point 
> of contact, that's fine, or you can just delete the entry. My name is 
> still in the source, but with the old address. It'd great if the 
> address in source was updated. 

cheers, Joe


^ permalink raw reply

* Re: hard_start_xmit struct sk_buff read-only?
From: Matti Linnanvuori @ 2007-12-22  9:18 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev
In-Reply-To: <476CCFBA.2010305@garzik.org>


--- Jeff Garzik <jeff@garzik.org> schrieb:

> This begs the question, what are you trying to do? 
> ;-)

I have seen network drivers whose hard_start_xmit
function calls skb_push or skb_put to extend the used
data area of the struct skb_buff passed to the
function. That avoids allocating a new buffer and
copying the whole data area to it. The drivers
apparently do that because the device expects a
specific header or suffix in the DMA buffer it
handles.

> In terms of object lifetimes, control passes to the
> net driver when 
> ->hard_start_xmit() is called, but that does not
> mean you can freely 
> scribble over things -- the skb may have been
> cloned, its destructor 
> callback still needs to be called (via
> dev_kfree_skb), etc.

So I assume modifying the data or header area of
struct sk_buff is incorrect because it can break
cloned buffers.


      Heute schon einen Blick in die Zukunft von E-Mails wagen? www.yahoo.de/mail

^ permalink raw reply

* Re: skbuff data pointer alignment requirement
From: Jarek Poplawski @ 2007-12-22 11:00 UTC (permalink / raw)
  To: Keyur Chudgar; +Cc: netdev
In-Reply-To: <a967d7d00712201712m6981373ife9a333be7d343e0@mail.gmail.com>

Keyur Chudgar wrote, On 12/21/2007 02:12 AM:
...

> If some hardware requirements, for example is, they need to have 256
> bytes aligned address for them to do the DMA, no matter what the
> packet size is. In this kind of cases, can you guide me what should I
> do? Is there any way already in Linux I can do this?

...

> In the above specified situation, I can define SKB_ADDR_MIN_ALIGN =
> 256 in my Makefile or I don't define it at all if I am okay with
> default alignment size.

Do you mean hardware requirements of an architecture or a specific driver?
So, if you have more than one network card, is it needed by all of them,
while other (not network) drivers are happy with default allocations?

Regards,
Jarek P.

^ permalink raw reply

* Re: [RFC PATCH v9 06/18] LSM: Add inet_sys_snd_skb() LSM hook (fwd)
From: Paul Moore @ 2007-12-22 14:54 UTC (permalink / raw)
  To: James Morris; +Cc: netdev, David S. Miller, Herbert Xu
In-Reply-To: <Xine.LNX.4.64.0712221147320.30005@us.intercode.com.au>

On Friday 21 December 2007 7:51:26 pm James Morris wrote:
> This is part of a large patchset which finally "fixes" labeled networking,
> which we're hoping to get into 2.6.25.
>
> Thread @ http://thread.gmane.org/gmane.linux.kernel.lsm/4894
>
> The patch below is the only one which is not self-contained & impacts on
> core networking code.
>
> If anyone has any objections or comments on this patch, please let us
> know.

Also, for the record, this is the same patch that was posted earlier in the 
week in an attempt to solicit comments.

> ---------- Forwarded message ----------
> Date: Fri, 21 Dec 2007 12:09:28 -0500
> From: Paul Moore <paul.moore@hp.com>
> To: selinux@tycho.nsa.gov, linux-security-module@vger.kernel.org
> Cc: vyekkirala@trustedcs.com, chanson@trustedcs.com
> Subject: [RFC PATCH v9 06/18] LSM: Add inet_sys_snd_skb() LSM hook
>
> Add an inet_sys_snd_skb() LSM hook to allow the LSM to provide packet level
> access control for all outbound packets.  Using the existing postroute_last
> netfilter hook turns out to be problematic as it is can be invoked multiple
> times for a single packet, e.g. individual IPsec transforms, adding
> unwanted overhead and complicating the security policy.
>
> Signed-off-by: Paul Moore <paul.moore@hp.com>
> ---
>
>  include/linux/security.h |   11 +++++++++++
>  net/ipv4/ip_output.c     |    7 +++++++
>  net/ipv6/ip6_output.c    |    5 +++++
>  security/dummy.c         |    8 +++++++-
>  security/security.c      |    6 ++++++
>  5 files changed, 36 insertions(+), 1 deletions(-)
>
> diff --git a/include/linux/security.h b/include/linux/security.h
> index db19c92..1b8d332 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -876,6 +876,10 @@ struct request_sock;
>   *     Sets the connection's peersid to the secmark on skb.
>   * @req_classify_flow:
>   *	Sets the flow's sid to the openreq sid.
> + * @inet_sys_snd_skb:
> + *	Check permissions on outgoing network packets.
> + *	@skb is the packet to check
> + *	@family is the packet's address family
>   *
>   * Security hooks for XFRM operations.
>   *
> @@ -1416,6 +1420,7 @@ struct security_operations {
>  	void (*inet_csk_clone)(struct sock *newsk, const struct request_sock
> *req); void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb);
> void (*req_classify_flow)(const struct request_sock *req, struct flowi
> *fl); +	int (*inet_sys_snd_skb)(struct sk_buff *skb, int family);
>  #endif	/* CONFIG_SECURITY_NETWORK */
>
>  #ifdef CONFIG_SECURITY_NETWORK_XFRM
> @@ -2328,6 +2333,7 @@ void security_sk_free(struct sock *sk);
>  void security_sk_clone(const struct sock *sk, struct sock *newsk);
>  void security_sk_classify_flow(struct sock *sk, struct flowi *fl);
>  void security_req_classify_flow(const struct request_sock *req, struct
> flowi *fl); +int security_inet_sys_snd_skb(struct sk_buff *skb, int
> family);
>  void security_sock_graft(struct sock*sk, struct socket *parent);
>  int security_inet_conn_request(struct sock *sk,
>  			struct sk_buff *skb, struct request_sock *req);
> @@ -2471,6 +2477,11 @@ static inline void security_req_classify_flow(const
> struct request_sock *req, st {
>  }
>
> +static inline int security_inet_sys_snd_skb(struct sk_buff *skb, int
> family) +{
> +	return 0;
> +}
> +
>  static inline void security_sock_graft(struct sock* sk, struct socket
> *parent) {
>  }
> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index fd99fbd..82a7297 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -204,6 +204,8 @@ static inline int ip_skb_dst_mtu(struct sk_buff *skb)
>
>  static int ip_finish_output(struct sk_buff *skb)
>  {
> +	int err;
> +
>  #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
>  	/* Policy lookup after SNAT yielded a new policy */
>  	if (skb->dst->xfrm != NULL) {
> @@ -211,6 +213,11 @@ static int ip_finish_output(struct sk_buff *skb)
>  		return dst_output(skb);
>  	}
>  #endif
> +
> +	err = security_inet_sys_snd_skb(skb, AF_INET);
> +	if (err)
> +		return err;
> +
>  	if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
>  		return ip_fragment(skb, ip_finish_output2);
>  	else
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 6338a9c..44ddf32 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -72,8 +72,13 @@ static __inline__ void ipv6_select_ident(struct sk_buff
> *skb, struct frag_hdr *f
>
>  static int ip6_output_finish(struct sk_buff *skb)
>  {
> +	int err;
>  	struct dst_entry *dst = skb->dst;
>
> +	err = security_inet_sys_snd_skb(skb, AF_INET6);
> +	if (err)
> +		return err;
> +
>  	if (dst->hh)
>  		return neigh_hh_output(dst->hh, skb);
>  	else if (dst->neighbour)
> diff --git a/security/dummy.c b/security/dummy.c
> index 0b62f95..384979a 100644
> --- a/security/dummy.c
> +++ b/security/dummy.c
> @@ -848,6 +848,11 @@ static inline void dummy_req_classify_flow(const
> struct request_sock *req, struct flowi *fl)
>  {
>  }
> +
> +static inline int dummy_inet_sys_snd_skb(struct sk_buff *skb, int family)
> +{
> +	return 0;
> +}
>  #endif	/* CONFIG_SECURITY_NETWORK */
>
>  #ifdef CONFIG_SECURITY_NETWORK_XFRM
> @@ -1122,7 +1127,8 @@ void security_fixup_ops (struct security_operations
> *ops) set_to_dummy_if_null(ops, inet_csk_clone);
>  	set_to_dummy_if_null(ops, inet_conn_established);
>  	set_to_dummy_if_null(ops, req_classify_flow);
> - #endif	/* CONFIG_SECURITY_NETWORK */
> +	set_to_dummy_if_null(ops, inet_sys_snd_skb);
> +#endif	/* CONFIG_SECURITY_NETWORK */
>  #ifdef  CONFIG_SECURITY_NETWORK_XFRM
>  	set_to_dummy_if_null(ops, xfrm_policy_alloc_security);
>  	set_to_dummy_if_null(ops, xfrm_policy_clone_security);
> diff --git a/security/security.c b/security/security.c
> index 3bdcada..7f55459 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -961,6 +961,12 @@ void security_req_classify_flow(const struct
> request_sock *req, struct flowi *fl }
>  EXPORT_SYMBOL(security_req_classify_flow);
>
> +int security_inet_sys_snd_skb(struct sk_buff *skb, int family)
> +{
> +	return security_ops->inet_sys_snd_skb(skb, family);
> +}
> +EXPORT_SYMBOL(security_inet_sys_snd_skb);
> +
>  void security_sock_graft(struct sock *sk, struct socket *parent)
>  {
>  	security_ops->sock_graft(sk, parent);
>
> -
> To unsubscribe from this list: send the line "unsubscribe
> linux-security-module" in the body of a message to
> majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
paul moore
linux security @ hp

^ permalink raw reply

* [PATCH] Documentation: add a guideline for hard_start_xmit method not to modify SKB
From: Matti Linnanvuori @ 2007-12-22 17:22 UTC (permalink / raw)
  To: jgarzik, netdev

From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>

Add a guideline for hard_start_xmit method not to
modify SKB.

Signed-off-by: Matti Linnanvuori
<mattilinnanvuori@yahoo.com>

---


--- a/Documentation/networking/driver.txt	2007-12-22
18:50:28.062169500 +0200
+++ b/Documentation/networking/driver.txt	2007-12-22
19:10:58.726566000 +0200

@@ -74,6 +74,9 @@ Transmit path guidelines:
    If you return 1 from the hard_start_xmit method,
you must not keep
    any reference to that SKB and you must not attempt
to free it up.
 
+4) A hard_start_xmit method must not modify the
cloned parts of the
+   SKB.
+
 Probing guidelines:
 
 1) Any hardware layer address you obtain for your
device should



      Machen Sie Yahoo! zu Ihrer Startseite. Los geht's: 
http://de.yahoo.com/set

^ permalink raw reply

* [PATCH 1/8] Fix sparse warning: returning void-valued expression
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>

rndis_unbind and usbnet_cdc_unbind don't return anything.

Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_host.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 1ebe325..96ef6a9 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -585,7 +585,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
 		kfree(halt);
 	}
 
-	return usbnet_cdc_unbind(dev, intf);
+	usbnet_cdc_unbind(dev, intf);
 }
 
 /*
-- 
1.5.2.5

^ permalink raw reply related

* [PATCH 4/8] [PATCH] Halt device if rndis_bind fails.
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Jussi Kivilinna
In-Reply-To: <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>

From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>

When bind fails after device was initialized, shutdown device properly
by sending RNDIS_MSG_HALT.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_host.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 42b161c..c686025 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -467,6 +467,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 		struct rndis_query_c	*get_c;
 		struct rndis_set	*set;
 		struct rndis_set_c	*set_c;
+		struct rndis_halt	*halt;
 	} u;
 	u32			tmp;
 	int			reply_len;
@@ -517,7 +518,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 				"dev can't take %u byte packets (max %u)\n",
 				dev->hard_mtu, tmp);
 			retval = -EINVAL;
-			goto fail_and_release;
+			goto halt_fail_and_release;
 		}
 		dev->hard_mtu = tmp;
 		net->mtu = dev->hard_mtu - net->hard_header_len;
@@ -539,7 +540,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 			48, (void **) &bp, &reply_len);
 	if (unlikely(retval< 0)) {
 		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
-		goto fail_and_release;
+		goto halt_fail_and_release;
 	}
 	memcpy(net->dev_addr, bp, ETH_ALEN);
 
@@ -555,7 +556,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 	retval = rndis_command(dev, u.header);
 	if (unlikely(retval < 0)) {
 		dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
-		goto fail_and_release;
+		goto halt_fail_and_release;
 	}
 
 	retval = 0;
@@ -563,6 +564,11 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 	kfree(u.buf);
 	return retval;
 
+halt_fail_and_release:
+	memset(u.halt, 0, sizeof *u.halt);
+	u.halt->msg_type = RNDIS_MSG_HALT;
+	u.halt->msg_len = ccpu2(sizeof *u.halt);
+	(void) rndis_command(dev, (void *)u.halt);
 fail_and_release:
 	usb_set_intfdata(info->data, NULL);
 	usb_driver_release_interface(driver_of(intf), info->data);
-- 
1.5.2.5

^ permalink raw reply related

* [PATCH 5/8] Fix rndis packet filter flags.
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Jussi Kivilinna
In-Reply-To: <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>

From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>

RNDIS packet filter flags are not exactly the same as CDC flags
so we cannot reuse them.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_host.c |   23 ++++++++++++++++++++++-
 1 files changed, 22 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index c686025..3c116f9 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -256,6 +256,27 @@ struct rndis_keepalive_c {	/* IN (optionally OUT) */
 #define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
 #define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
 
+/* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */
+#define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
+#define RNDIS_PACKET_TYPE_MULTICAST		ccpu2(0x00000002)
+#define RNDIS_PACKET_TYPE_ALL_MULTICAST		ccpu2(0x00000004)
+#define RNDIS_PACKET_TYPE_BROADCAST		ccpu2(0x00000008)
+#define RNDIS_PACKET_TYPE_SOURCE_ROUTING	ccpu2(0x00000010)
+#define RNDIS_PACKET_TYPE_PROMISCUOUS		ccpu2(0x00000020)
+#define RNDIS_PACKET_TYPE_SMT			ccpu2(0x00000040)
+#define RNDIS_PACKET_TYPE_ALL_LOCAL		ccpu2(0x00000080)
+#define RNDIS_PACKET_TYPE_GROUP			ccpu2(0x00001000)
+#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL	ccpu2(0x00002000)
+#define RNDIS_PACKET_TYPE_FUNCTIONAL		ccpu2(0x00004000)
+#define RNDIS_PACKET_TYPE_MAC_FRAME		ccpu2(0x00008000)
+
+/* default filter used with RNDIS devices */
+#define RNDIS_DEFAULT_FILTER ( \
+	RNDIS_PACKET_TYPE_DIRECTED | \
+	RNDIS_PACKET_TYPE_BROADCAST | \
+	RNDIS_PACKET_TYPE_ALL_MULTICAST | \
+	RNDIS_PACKET_TYPE_PROMISCUOUS )
+
 /*
  * RNDIS notifications from device: command completion; "reverse"
  * keepalives; etc
@@ -551,7 +572,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 	u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
 	u.set->len = ccpu2(4);
 	u.set->offset = ccpu2((sizeof *u.set) - 8);
-	*(__le32 *)(u.buf + sizeof *u.set) = ccpu2(DEFAULT_FILTER);
+	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
 
 	retval = rndis_command(dev, u.header);
 	if (unlikely(retval < 0)) {
-- 
1.5.2.5

^ permalink raw reply related

* [PATCH 8/8] [PATCH] Use wlan device name for RNDIS wireless devices
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Jussi Kivilinna
In-Reply-To: <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>

From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>

Use wlan device name for RNDIS wireless devices.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_wext.c |    2 +-
 drivers/net/usb/usbnet.c     |    3 +++
 drivers/net/usb/usbnet.h     |    2 ++
 3 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_wext.c b/drivers/net/usb/rndis_wext.c
index a9ce944..1c28b2a 100644
--- a/drivers/net/usb/rndis_wext.c
+++ b/drivers/net/usb/rndis_wext.c
@@ -2166,7 +2166,7 @@ static int rndis_wext_reset(struct usbnet *dev)
 
 struct driver_info rndis_wext_info = {
 	.description =	"Wireless RNDIS device",
-	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
 	.bind =		rndis_wext_bind,
 	.unbind =	rndis_wext_unbind,
 	.status =	rndis_status,
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 8ed1fc5..a2a2d5e 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1204,6 +1204,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 		if ((dev->driver_info->flags & FLAG_ETHER) != 0
 				&& (net->dev_addr [0] & 0x02) == 0)
 			strcpy (net->name, "eth%d");
+		/* WLAN devices should always be named "wlan%d" */
+		if ((dev->driver_info->flags & FLAG_WLAN) != 0)
+			strcpy(net->name, "wlan%d");
 
 		/* maybe the remote can't receive an Ethernet MTU */
 		if (net->mtu > (dev->hard_mtu - net->hard_header_len))
diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
index 83860a0..5c98ddc 100644
--- a/drivers/net/usb/usbnet.h
+++ b/drivers/net/usb/usbnet.h
@@ -88,6 +88,8 @@ struct driver_info {
 #define FLAG_ETHER	0x0020		/* maybe use "eth%d" names */
 
 #define FLAG_FRAMING_AX 0x0040		/* AX88772/178 packets */
+#define FLAG_WLAN	0x0080		/* use "wlan%d" names */
+
 
 	/* init device ... can sleep, or cause probe() failure */
 	int	(*bind)(struct usbnet *, struct usb_interface *);
-- 
1.5.2.5

^ permalink raw reply related

* [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless
In-Reply-To: <11983602942818-git-send-email-bjd@jooz.net>

Just as ActiveSync devices, some regular RNDIS devices also lack
the CDC descriptors (e.g. devices based on BCM4320 WLAN chip).
This patch hardwires the CDC descriptors for all RNDIS style devices
when they are missing.

Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/cdc_ether.c |   10 +++++-----
 1 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index a42acc3..97c17bb 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -228,15 +228,16 @@ next_desc:
 		buf += buf [0];
 	}
 
-	/* Microsoft ActiveSync based RNDIS devices lack the CDC descriptors,
-	 * so we'll hard-wire the interfaces and not check for descriptors.
+	/* Microsoft ActiveSync based and some regular RNDIS devices lack the
+	 * CDC descriptors, so we'll hard-wire the interfaces and not check
+	 * for descriptors.
 	 */
-	if (is_activesync(&intf->cur_altsetting->desc) && !info->u) {
+	if (rndis && !info->u) {
 		info->control = usb_ifnum_to_if(dev->udev, 0);
 		info->data = usb_ifnum_to_if(dev->udev, 1);
 		if (!info->control || !info->data) {
 			dev_dbg(&intf->dev,
-				"activesync: master #0/%p slave #1/%p\n",
+				"rndis: master #0/%p slave #1/%p\n",
 				info->control,
 				info->data);
 			goto bad_desc;
@@ -316,7 +317,6 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
 }
 EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
 
-\f
 /*-------------------------------------------------------------------------
  *
  * Communications Device Class, Ethernet Control model
-- 
1.5.2.5


^ permalink raw reply related

* [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless, Jussi Kivilinna
In-Reply-To: <11983602942818-git-send-email-bjd@jooz.net>

From: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>

rndis_command requires the caller to pass in a buffer of at least 1KB.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/rndis_host.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 96ef6a9..42b161c 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -577,7 +577,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
 	struct rndis_halt	*halt;
 
 	/* try to clear any rndis state/activity (no i/o from stack!) */
-	halt = kzalloc(sizeof *halt, GFP_KERNEL);
+	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
 	if (halt) {
 		halt->msg_type = RNDIS_MSG_HALT;
 		halt->msg_len = ccpu2(sizeof *halt);
-- 
1.5.2.5


^ permalink raw reply related

* [PATCH 7/8] Add Wireless Extensions to rndis_host
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless
In-Reply-To: <11983602942818-git-send-email-bjd@jooz.net>

The bulk of this patch is the addition of a new file that
implements the wireless extensions for RNDIS devices.
The rest are some smaller changes to usbnet and rndis_host
to hook the wireless extensions into them:
* a private data pointer is added to usbnet.
* a callback is added to driver_info to signal link state changes.
* a physical medium type check is added to rndis_bind to check for
  wireless lan devices and turn on the wireless extensions.
* and finally a Kconfig option to enable/disable this all.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/Kconfig      |    7 +
 drivers/net/usb/Makefile     |    4 +
 drivers/net/usb/rndis_base.c |   50 +-
 drivers/net/usb/rndis_host.h |   18 +
 drivers/net/usb/rndis_wext.c | 2177 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/usbnet.h     |    4 +
 6 files changed, 2255 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/usb/rndis_wext.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index a12c9c4..cc38294 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -239,6 +239,13 @@ config USB_NET_RNDIS_HOST
 	  The protocol specification is incomplete, and is controlled by
 	  (and for) Microsoft; it isn't an "Open" ecosystem or market.
 
+config USB_NET_RNDIS_WEXT
+	boolean "Wireless Extensions for RNDIS host driver"
+	depends on USB_NET_RNDIS_HOST && WLAN_80211 && EXPERIMENTAL
+	select WIRELESS_EXT
+	help
+	  This option enables support for RNDIS based wireless LAN devices.
+
 config USB_NET_CDC_SUBSET
 	tristate "Simple USB Network Links (CDC Ethernet subset)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 611ab62..83c14f1 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -14,6 +14,10 @@ obj-$(CONFIG_USB_NET_NET1080)	+= net1080.o
 obj-$(CONFIG_USB_NET_PLUSB)	+= plusb.o
 obj-$(CONFIG_USB_NET_RNDIS_HOST)	+= rndis_host.o
 rndis_host-objs			+= rndis_base.o
+ifeq ($(CONFIG_USB_NET_RNDIS_WEXT),y)
+rndis_host-objs += rndis_wext.o
+endif
+
 obj-$(CONFIG_USB_NET_CDC_SUBSET)	+= cdc_subset.o
 obj-$(CONFIG_USB_NET_ZAURUS)	+= zaurus.o
 obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
diff --git a/drivers/net/usb/rndis_base.c b/drivers/net/usb/rndis_base.c
index f3b0c00..97faaed 100644
--- a/drivers/net/usb/rndis_base.c
+++ b/drivers/net/usb/rndis_base.c
@@ -148,10 +148,26 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
 					request_id, xid);
 				/* then likely retry */
 			} else switch (buf->msg_type) {
-			case RNDIS_MSG_INDICATE: {	/* fault */
-				// struct rndis_indicate *msg = (void *)buf;
-				dev_info(&info->control->dev,
-					"rndis fault indication\n");
+			case RNDIS_MSG_INDICATE: {	/* fault/event */
+				struct rndis_indicate *msg = (void *)buf;
+
+				switch (msg->status) {
+				case RNDIS_STATUS_MEDIA_CONNECT:
+					dev_info(&info->control->dev,
+						"rndis media connect\n");
+					if (dev->driver_info->link_change)
+					    dev->driver_info->link_change(dev, 1);
+					break;
+				case RNDIS_STATUS_MEDIA_DISCONNECT:
+					dev_info(&info->control->dev,
+						"rndis media disconnect\n");
+					if (dev->driver_info->link_change)
+					    dev->driver_info->link_change(dev, 0);
+					break;
+				default:
+					dev_info(&info->control->dev,
+						"rndis indication\n");
+				}
 				}
 				break;
 			case RNDIS_MSG_KEEPALIVE: {	/* ping */
@@ -271,7 +287,7 @@ int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 		struct rndis_set_c	*set_c;
 		struct rndis_halt	*halt;
 	} u;
-	u32			tmp;
+	u32			tmp, *phym;
 	int			reply_len;
 	unsigned char		*bp;
 
@@ -336,6 +352,30 @@ int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 		dev->hard_mtu, tmp, dev->rx_urb_size,
 		1 << le32_to_cpu(u.init_c->packet_alignment));
 
+	/* Check physical medium */
+	reply_len = sizeof *phym;
+	retval = rndis_query(dev, intf, u.buf, OID_GEN_PHYSICAL_MEDIUM,
+			0, (void **) &phym, &reply_len);
+	if (retval != 0) {
+		dev_err(&intf->dev, "rndis get physical medium, %d\n", retval);
+		tmp = RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED;
+	} else
+		tmp = *phym;
+
+	if (tmp == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
+#ifdef CONFIG_USB_NET_RNDIS_WEXT
+		dev_info(&info->control->dev, "RNDIS wlan device,"
+				" turning on wireless extensions\n");
+		dev->driver_info = &rndis_wext_info;
+		retval = dev->driver_info->bind(dev, intf);
+		if (retval < 0)
+			goto halt_fail_and_release;
+#else
+		dev_info(&info->control->dev, "RNDIS wlan device,"
+				" wireless extensions not available\n");
+#endif
+	}
+
 	/* Get designated host ethernet address */
 	reply_len = ETH_ALEN;
 	retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
diff --git a/drivers/net/usb/rndis_host.h b/drivers/net/usb/rndis_host.h
index 56de532..6427315 100644
--- a/drivers/net/usb/rndis_host.h
+++ b/drivers/net/usb/rndis_host.h
@@ -82,6 +82,18 @@ struct rndis_msg_hdr {
 #define	RNDIS_STATUS_MEDIA_CONNECT	ccpu2(0x4001000b)
 #define	RNDIS_STATUS_MEDIA_DISCONNECT	ccpu2(0x4001000c)
 
+/* codes for OID_GEN_PHYSICAL_MEDIUM */
+#define RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED       ccpu2(0x00000000)
+#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN      ccpu2(0x00000001)
+#define RNDIS_PHYSICAL_MEDIUM_CABLE_MODEM       ccpu2(0x00000002)
+#define RNDIS_PHYSICAL_MEDIUM_PHONE_LINE        ccpu2(0x00000003)
+#define RNDIS_PHYSICAL_MEDIUM_POWER_LINE        ccpu2(0x00000004)
+#define RNDIS_PHYSICAL_MEDIUM_DSL               ccpu2(0x00000005)
+#define RNDIS_PHYSICAL_MEDIUM_FIBRE_CHANNEL     ccpu2(0x00000006)
+#define RNDIS_PHYSICAL_MEDIUM_1394              ccpu2(0x00000007)
+#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_WAN      ccpu2(0x00000008)
+#define RNDIS_PHYSICAL_MEDIUM_MAX               ccpu2(0x00000009)
+
 
 struct rndis_data_hdr {
 	__le32	msg_type;		/* RNDIS_MSG_PACKET */
@@ -222,6 +234,7 @@ struct rndis_keepalive_c {	/* IN (optionally OUT) */
 #define OID_802_3_PERMANENT_ADDRESS	ccpu2(0x01010101)
 #define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
 #define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
+#define OID_GEN_PHYSICAL_MEDIUM		ccpu2(0x00010202)
 
 /* packet filter bits used by NDIS_OID_PACKET_FILTER */
 #define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
@@ -252,5 +265,10 @@ extern int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb);
 extern struct sk_buff *
 rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags);
 
+#ifdef CONFIG_USB_NET_RNDIS_WEXT
+/* from rndis_wext.c */
+extern struct driver_info rndis_wext_info;
+#endif
+
 #endif	/* __RNDIS_HOST_H */
 
diff --git a/drivers/net/usb/rndis_wext.c b/drivers/net/usb/rndis_wext.c
new file mode 100644
index 0000000..a9ce944
--- /dev/null
+++ b/drivers/net/usb/rndis_wext.c
@@ -0,0 +1,2177 @@
+/*
+ * Wireless Extensions for RNDIS host
+ * Copyright (C) 2007 by Bjorge Dijkstra (bjd@jooz.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Portions of this file are based on NDISwrapper project,
+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ * http://ndiswrapper.sourceforge.net/
+ */
+
+// #define	DEBUG			// error path messages, extra info
+// #define	VERBOSE			// more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+
+
+#include "usbnet.h"
+#include "rndis_host.h"
+
+/* various RNDIS OID defs */
+#define OID_GEN_LINK_SPEED			ccpu2(0x00010107)
+#define OID_GEN_RNDIS_CONFIG_PARAMETER		ccpu2(0x0001021b)
+
+#define OID_802_3_PERMANENT_ADDRESS		ccpu2(0x01010101)
+#define OID_802_3_CURRENT_ADDRESS		ccpu2(0x01010102)
+#define OID_802_3_MULTICAST_LIST        	ccpu2(0x01010103)
+
+#define OID_802_11_BSSID			ccpu2(0x0d010101)
+#define OID_802_11_SSID				ccpu2(0x0d010102)
+#define OID_802_11_INFRASTRUCTURE_MODE		ccpu2(0x0d010108)
+#define OID_802_11_ADD_WEP			ccpu2(0x0d010113)
+#define OID_802_11_REMOVE_WEP			ccpu2(0x0d010114)
+#define OID_802_11_DISASSOCIATE			ccpu2(0x0d010115)
+#define OID_802_11_AUTHENTICATION_MODE		ccpu2(0x0d010118)
+#define OID_802_11_PRIVACY_FILTER		ccpu2(0x0d010119)
+#define OID_802_11_BSSID_LIST_SCAN		ccpu2(0x0d01011a)
+#define OID_802_11_ENCRYPTION_STATUS		ccpu2(0x0d01011b)
+#define OID_802_11_ADD_KEY			ccpu2(0x0d01011d)
+#define OID_802_11_REMOVE_KEY			ccpu2(0x0d01011e)
+#define OID_802_11_PMKID			ccpu2(0x0d010123)
+#define OID_802_11_NETWORK_TYPES_SUPPORTED	ccpu2(0x0d010203)
+#define OID_802_11_NETWORK_TYPE_IN_USE		ccpu2(0x0d010204)
+#define OID_802_11_TX_POWER_LEVEL		ccpu2(0x0d010205)
+#define OID_802_11_RSSI				ccpu2(0x0d010206)
+#define OID_802_11_RSSI_TRIGGER			ccpu2(0x0d010207)
+#define OID_802_11_FRAGMENTATION_THRESHOLD	ccpu2(0x0d010209)
+#define OID_802_11_RTS_THRESHOLD		ccpu2(0x0d01020a)
+#define OID_802_11_SUPPORTED_RATES		ccpu2(0x0d01020e)
+#define OID_802_11_CONFIGURATION		ccpu2(0x0d010211)
+#define OID_802_11_BSSID_LIST			ccpu2(0x0d010217)
+#define OID_802_11_STATISTICS			ccpu2(0x0d020212)
+
+
+/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */
+#define	WL_NOISE	-96	/* typical noise level in dBm */
+#define	WL_SIGMAX	-32	/* typical maximum signal level in dBm */
+
+
+/* Assume that Broadcom 4320 (only chipset at time of writing known to be
+ * based on wireless rndis) has default txpower of 13dBm as Linksys WUSB54GSC.
+ * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications.
+ *   13dBm == 19.9mW
+ */
+#define BCM4320_DEFAULT_TXPOWER 20
+
+
+/* codes for "status" field of completion messages */
+#define RNDIS_STATUS_ADAPTER_NOT_READY		ccpu2(0xc0010011)
+#define RNDIS_STATUS_ADAPTER_NOT_OPEN		ccpu2(0xc0010012)
+
+
+/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c
+ * slightly modified for datatype endianess, etc
+ */
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+struct NDIS_802_11_SSID {
+	__le32 SsidLength;
+	u8 Ssid[NDIS_802_11_LENGTH_SSID];
+};
+
+enum NDIS_802_11_NETWORK_TYPE {
+	Ndis802_11FH,
+	Ndis802_11DS,
+	Ndis802_11OFDM5,
+	Ndis802_11OFDM24,
+	Ndis802_11NetworkTypeMax
+};
+
+struct NDIS_802_11_CONFIGURATION_FH {
+	__le32 Length;
+	__le32 HopPattern;
+	__le32 HopSet;
+	__le32 DwellTime;
+};
+
+struct NDIS_802_11_CONFIGURATION {
+	__le32 Length;
+	__le32 BeaconPeriod;
+	__le32 ATIMWindow;
+	__le32 DSConfig;
+	struct NDIS_802_11_CONFIGURATION_FH FHConfig;
+};
+
+struct NDIS_802_11_STATS {
+	__le32 Length;
+	__le64 TxFrag;
+	__le64 TxMultiFrag;
+	__le64 Failed;
+	__le64 Retry;
+	__le64 MultiRetry;
+	__le64 RtssSucc;
+	__le64 RtssFail;
+	__le64 AckFail;
+	__le64 FrameDup;
+	__le64 RxFrag;
+	__le64 RxMultiFrag;
+	__le64 FcsErr;
+	__le64 TkipLocalMicFailures;
+	__le64 TkipIcvErrors;
+	__le64 TkipCounterMeasuresInvoked;
+	__le64 TkipReplays;
+	__le64 CcmpFormatErrors;
+	__le64 CcmpReplays;
+	__le64 CcmpDecryptErrors;
+	__le64 FourwayHandshakeFailures;
+	__le64 WepUndecryptableCount;
+	__le64 WepIcvErrorcount;
+	__le64 DecryptSuccessCount;
+	__le64 DecryptFailureCount;
+} __attribute__((packed));
+
+enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+	Ndis802_11IBSS,
+	Ndis802_11Infrastructure,
+	Ndis802_11AutoUnknown,
+	Ndis802_11InfrastructureMax
+};
+
+enum NDIS_802_11_AUTHENTICATION_MODE {
+	Ndis802_11AuthModeOpen,
+	Ndis802_11AuthModeShared,
+	Ndis802_11AuthModeAutoSwitch,
+	Ndis802_11AuthModeWPA,
+	Ndis802_11AuthModeWPAPSK,
+	Ndis802_11AuthModeWPANone,
+	Ndis802_11AuthModeWPA2,
+	Ndis802_11AuthModeWPA2PSK,
+	Ndis802_11AuthModeMax
+};
+
+enum NDIS_802_11_ENCRYPTION_STATUS {
+	Ndis802_11WEPEnabled,
+	Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+	Ndis802_11WEPDisabled,
+	Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+	Ndis802_11WEPKeyAbsent,
+	Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+	Ndis802_11WEPNotSupported,
+	Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+	Ndis802_11Encryption2Enabled,
+	Ndis802_11Encryption2KeyAbsent,
+	Ndis802_11Encryption3Enabled,
+	Ndis802_11Encryption3KeyAbsent
+};
+
+enum NDIS_802_11_PRIVACY_FILTER {
+	Ndis802_11PrivFilterAcceptAll,
+	Ndis802_11PrivFilter8021xWEP
+};
+
+struct NDIS_WLAN_BSSID_EX {
+	__le32 Length;
+	u8 MacAddress[6];
+	u8 Padding[2];
+	struct NDIS_802_11_SSID Ssid;
+	__le32 Privacy;
+	__le32 Rssi;
+	enum NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+	struct NDIS_802_11_CONFIGURATION Configuration;
+	enum NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+	u8 SupportedRates[NDIS_802_11_LENGTH_RATES_EX];
+	__le32 IELength;
+	u8 IEs[0];
+};
+
+struct NDIS_802_11_BSSID_LIST_EX {
+	__le32 NumberOfItems;
+	struct NDIS_WLAN_BSSID_EX Bssid[0];
+};
+
+struct NDIS_802_11_FIXED_IEs {
+	u8 Timestamp[8];
+	__le16 BeaconInterval;
+	__le16 Capabilities;
+};
+
+struct NDIS_802_11_WEP {
+	__le32 Length;
+	__le32 KeyIndex;
+	__le32 KeyLength;
+	u8 KeyMaterial[0];
+};
+
+struct NDIS_802_11_KEY {
+	__le32 Length;
+	__le32 KeyIndex;
+	__le32 KeyLength;
+	u8 Bssid[6];
+	u8 Padding[6];
+	u8 KeyRSC[8];
+	u8 KeyMaterial[32];
+};
+
+struct NDIS_802_11_REMOVE_KEY {
+	__le32 Length;
+	__le32 KeyIndex;
+	u8 Bssid[6];
+};
+
+struct RNDIS_CONFIG_PARAMETER_INFOBUFFER {
+	__le32 ParameterNameOffset;
+	__le32 ParameterNameLength;
+	__le32 ParameterType;
+	__le32 ParameterValueOffset;
+	__le32 ParameterValueLength;
+} __attribute__((packed));
+
+/*
+ *  private data
+ */
+#define NET_TYPE_11FB	0
+
+#define CAP_MODE_80211A		1
+#define CAP_MODE_80211B		2
+#define CAP_MODE_80211G		4
+#define CAP_MODE_MASK		7
+#define CAP_SUPPORT_TXPOWER	8
+
+/* RNDIS device private data */
+struct rndis_wext_private {
+	char name[32];
+
+	struct usbnet *usbdev;
+
+	struct workqueue_struct *workqueue;
+	struct delayed_work stats_work;
+	struct work_struct work;
+	struct mutex command_lock;
+	struct mutex stats_lock;
+
+	struct iw_statistics iwstats;
+	struct iw_statistics privstats;
+	int  nick_len;
+	char nick[32];
+
+	int caps;
+	int mode;
+	int radio_on;
+
+	/* general encryption stuff */
+	int privacy;
+
+	/* wep stuff */
+	int  wep_def_key;
+	int  wep_enabled;
+	char wep_keys[4][13];
+	int  wep_key_len[4];
+
+	/* wpa stuff */
+	int wpa_enabled;
+	int wpa_version;
+	int wpa_keymgmt;
+	int wpa_authalg;
+	int wpa_ie_len;
+	u8 *wpa_ie;
+	int wpa_cipher_pair;
+	int wpa_cipher_group;
+	struct NDIS_802_11_KEY wpa_keys[4];
+};
+
+
+static const int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+				2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+
+static const int rates_80211g[8] = { 6, 9, 12, 18, 24, 36, 48, 54 };
+
+static const int bcm4320_power_output[4] = { 25, 50, 75, 100 };
+
+static const unsigned char zero_bssid[ETH_ALEN] = {0,};
+
+
+static inline struct rndis_wext_private *get_rndis_wext_priv(struct usbnet *dev)
+{
+	return (struct rndis_wext_private *)dev->driver_priv;
+}
+
+
+static inline u32 get_bcm4320_power(struct rndis_wext_private *priv)
+{
+	return BCM4320_DEFAULT_TXPOWER;
+}
+
+
+/* translate error code */
+static int rndis_error_status(__le32 rndis_status)
+{
+	int ret = -EINVAL;
+	switch (rndis_status) {
+	case RNDIS_STATUS_SUCCESS:
+		ret = 0;
+		break;
+	case RNDIS_STATUS_FAILURE:
+	case RNDIS_STATUS_INVALID_DATA:
+		ret = -EINVAL;
+		break;
+	case RNDIS_STATUS_NOT_SUPPORTED:
+		ret = -EOPNOTSUPP;
+		break;
+	case RNDIS_STATUS_ADAPTER_NOT_READY:
+	case RNDIS_STATUS_ADAPTER_NOT_OPEN:
+		ret = -EBUSY;
+		break;
+	}
+	return ret;
+}
+
+
+static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_query	*get;
+		struct rndis_query_c	*get_c;
+	} u;
+	int ret, buflen;
+
+	buflen = *len + sizeof(*u.get);
+	if (buflen < CONTROL_BUFFER_SIZE)
+		buflen = CONTROL_BUFFER_SIZE;
+	u.buf = kmalloc(buflen, GFP_KERNEL);
+	if (!u.buf)
+		return -ENOMEM;
+	memset(u.get, 0, sizeof *u.get);
+	u.get->msg_type = RNDIS_MSG_QUERY;
+	u.get->msg_len = ccpu2(sizeof *u.get);
+	u.get->oid = oid;
+
+	mutex_lock(&priv->command_lock);
+	ret = rndis_command(dev, u.header);
+	mutex_unlock(&priv->command_lock);
+
+	if (ret == 0) {
+		ret = le32_to_cpu(u.get_c->len);
+		*len = (*len > ret) ? ret : *len;
+		memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len);
+		ret = rndis_error_status(u.get_c->status);
+	}
+
+	kfree(u.buf);
+	return ret;
+}
+
+
+static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_set	*set;
+		struct rndis_set_c	*set_c;
+	} u;
+	int ret, buflen;
+
+	buflen = len + sizeof(*u.set);
+	if (buflen < CONTROL_BUFFER_SIZE)
+		buflen = CONTROL_BUFFER_SIZE;
+	u.buf = kmalloc(buflen, GFP_KERNEL);
+	if (!u.buf)
+		return -ENOMEM;
+
+	memset(u.set, 0, sizeof *u.set);
+	u.set->msg_type = RNDIS_MSG_SET;
+	u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len);
+	u.set->oid = oid;
+	u.set->len = cpu_to_le32(len);
+	u.set->offset = ccpu2(sizeof(*u.set) - 8);
+	u.set->handle = ccpu2(0);
+	memcpy(u.buf + sizeof(*u.set), data, len);
+
+	mutex_lock(&priv->command_lock);
+	ret = rndis_command(dev, u.header);
+	mutex_unlock(&priv->command_lock);
+
+	if (ret == 0)
+		ret = rndis_error_status(u.set_c->status);
+
+	kfree(u.buf);
+	return ret;
+}
+
+
+/*
+ * Specs say that we can only set config parameters only soon after device
+ * initialization.
+ *   value_type: 0 = u32, 2 = unicode string
+ */
+static int rndis_set_config_parameter(struct usbnet *dev, char *param,
+						int value_type, void *value)
+{
+	struct RNDIS_CONFIG_PARAMETER_INFOBUFFER *infobuf;
+	int value_len, info_len, param_len, ret, i;
+	__le16 *unibuf;
+	__le32 *dst_value;
+
+	if (value_type == 0)
+		value_len = sizeof(__le32);
+	else if (value_type == 2)
+		value_len = strlen(value) * sizeof(__le16);
+	else
+		return -EINVAL;
+
+	param_len = strlen(param) * sizeof(__le16);
+	info_len = sizeof(*infobuf) + param_len + value_len;
+
+	infobuf = kmalloc(info_len + 12, GFP_KERNEL);
+	if (!infobuf)
+		return -ENOMEM;
+
+#ifdef DEBUG
+	/* extra 12 bytes are for padding (debug output) */
+	memset(infobuf, 0xCC, info_len + 12);
+#endif
+
+	if (value_type == 2)
+		devdbg(dev, "setting config parameter: %s, value: %s",
+						param, (u8 *)value);
+	else
+		devdbg(dev, "setting config parameter: %s, value: %d",
+						param, *(u32 *)value);
+
+	infobuf->ParameterNameOffset = cpu_to_le32(sizeof(*infobuf));
+	infobuf->ParameterNameLength = cpu_to_le32(param_len);
+	infobuf->ParameterType = cpu_to_le32(value_type);
+	infobuf->ParameterValueOffset = cpu_to_le32(sizeof(*infobuf) +
+								param_len);
+	infobuf->ParameterValueLength = cpu_to_le32(value_len);
+
+	/* simple string to unicode string conversion */
+	unibuf = (void *)infobuf + sizeof(*infobuf);
+	for (i = 0; i < param_len / sizeof(__le16); i++)
+		unibuf[i] = cpu_to_le16(param[i]);
+
+	if (value_type == 2) {
+		unibuf = (void *)infobuf + sizeof(*infobuf) + param_len;
+		for (i = 0; i < value_len / sizeof(__le16); i++)
+			unibuf[i] = cpu_to_le16(((u8 *)value)[i]);
+	} else {
+		dst_value = (void *)infobuf + sizeof(*infobuf) + param_len;
+		*dst_value = cpu_to_le32(*(u32 *)value);
+	}
+
+#ifdef DEBUG
+	devdbg(dev, "info buffer (len: %d):", info_len);
+	for (i = 0; i < info_len; i += 12) {
+		u32 *tmp = (u32 *)((u8 *)infobuf + i);
+		devdbg(dev, "%08X:%08X:%08X",
+			cpu_to_be32(tmp[0]),
+			cpu_to_be32(tmp[1]),
+			cpu_to_be32(tmp[2]));
+	}
+#endif
+
+	ret = rndis_set_oid(dev, OID_GEN_RNDIS_CONFIG_PARAMETER,
+							infobuf, info_len);
+	if (ret != 0)
+		devdbg(dev, "setting rndis config parameter failed, %d.", ret);
+
+	kfree(infobuf);
+	return ret;
+}
+
+static inline int rndis_set_config_parameter_str(struct usbnet *dev,
+						char *param, char *value)
+{
+	return(rndis_set_config_parameter(dev, param, 2, value));
+}
+
+static inline int rndis_set_config_parameter_u32(struct usbnet *dev,
+						char *param, u32 value)
+{
+	return(rndis_set_config_parameter(dev, param, 0, &value));
+}
+
+
+/*
+ * data conversion functions
+ */
+static inline int level_to_qual(int level)
+{
+	int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE);
+	return qual >= 0 ? (qual <= 100 ? qual : 100) : 0;
+}
+
+
+static inline void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq)
+{
+	freq->e = 0;
+	freq->i = 0;
+	freq->flags = 0;
+
+	/* see comment in wireless.h above the "struct iw_freq"
+	 * definition for an explanation of this if
+	 * NOTE: 1000000 is due to the kHz
+	 */
+	if (dsconfig > 1000000) {
+		freq->m = dsconfig / 10;
+		freq->e = 1;
+	} else
+		freq->m = dsconfig;
+
+	/* convert from kHz to Hz */
+	freq->e += 3;
+}
+
+
+static inline int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)
+{
+	if (freq->m < 1000 && freq->e == 0) {
+		if (freq->m >= 1 &&
+			freq->m <= (sizeof(freq_chan) / sizeof(freq_chan[0])))
+			*dsconfig = freq_chan[freq->m - 1] * 1000;
+		else
+			return -1;
+	} else {
+		int i;
+		*dsconfig = freq->m;
+		for (i = freq->e; i > 0; i--)
+			*dsconfig *= 10;
+		*dsconfig /= 1000;
+	}
+
+	return 0;
+}
+
+
+/*
+ * common functions
+ */
+static void set_default_iw_params(struct usbnet *dev)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	__le32 tmp;
+
+	priv->wpa_keymgmt = 0;
+	priv->wpa_version = 0;
+
+	tmp = cpu_to_le32(Ndis802_11Infrastructure);
+	rndis_set_oid(dev, OID_802_11_INFRASTRUCTURE_MODE, &tmp, sizeof(tmp));
+	tmp = cpu_to_le32(Ndis802_11EncryptionDisabled);
+	rndis_set_oid(dev, OID_802_11_ENCRYPTION_STATUS, &tmp, sizeof(tmp));
+	tmp = cpu_to_le32(Ndis802_11AuthModeOpen);
+	rndis_set_oid(dev, OID_802_11_AUTHENTICATION_MODE, &tmp, sizeof(tmp));
+	tmp = cpu_to_le32(Ndis802_11PrivFilter8021xWEP);
+	rndis_set_oid(dev, OID_802_11_PRIVACY_FILTER, &tmp, sizeof(tmp));
+}
+
+
+static int get_essid(struct usbnet *usbdev, struct NDIS_802_11_SSID *ssid)
+{
+	int ret, len;
+
+	len = sizeof(*ssid);
+	ret = rndis_query_oid(usbdev, OID_802_11_SSID, ssid, &len);
+
+	if (ret != 0)
+		ssid->SsidLength = 0;
+
+#ifdef DEBUG
+	{
+		unsigned char tmp[NDIS_802_11_LENGTH_SSID + 1];
+
+		memcpy(tmp, ssid->Ssid, le32_to_cpu(ssid->SsidLength));
+		tmp[le32_to_cpu(ssid->SsidLength)] = 0;
+		devdbg(usbdev, "get_essid: '%s', ret: %d", tmp, ret);
+	}
+#endif
+	return ret;
+}
+
+
+static int set_essid(struct usbnet *usbdev, struct NDIS_802_11_SSID *ssid)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int ret;
+
+	ret = rndis_set_oid(usbdev, OID_802_11_SSID, ssid, sizeof(*ssid));
+	if (ret == 0) {
+		priv->radio_on = 1;
+		devdbg(usbdev, "set_essid: radio_on = 1");
+	}
+
+	return ret;
+}
+
+
+static int get_bssid(struct usbnet *usbdev, unsigned char bssid[ETH_ALEN])
+{
+	int ret, len;
+
+	len = ETH_ALEN;
+	ret = rndis_query_oid(usbdev, OID_802_11_BSSID, bssid, &len);
+
+	if (ret != 0)
+		memset(bssid, 0, ETH_ALEN);
+
+	return ret;
+}
+
+
+static int is_associated(struct usbnet *usbdev)
+{
+	unsigned char bssid[ETH_ALEN];
+	int ret;
+
+	ret = get_bssid(usbdev, bssid);
+
+	return(ret == 0 && memcmp(bssid, zero_bssid, ETH_ALEN) != 0);
+}
+
+
+static int disassociate(struct usbnet *usbdev, int reset_ssid)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	struct NDIS_802_11_SSID ssid;
+	int i, ret = 0;
+
+	if (priv->radio_on) {
+		ret = rndis_set_oid(usbdev, OID_802_11_DISASSOCIATE, NULL, 0);
+		if (ret == 0) {
+			priv->radio_on = 0;
+			devdbg(usbdev, "disassociate: radio_on = 0");
+
+			if (reset_ssid)
+				msleep(100);
+		}
+	}
+
+	/* disassociate causes radio to be turned off; if reset_ssid
+	 * is given, set ssid to random to enable radio */
+	if (reset_ssid) {
+		ssid.SsidLength = cpu_to_le32(sizeof(ssid.Ssid));
+		get_random_bytes(&ssid.Ssid[2], sizeof(ssid.Ssid)-2);
+		ssid.Ssid[0] = 0x01;
+		ssid.Ssid[1] = 0xff;
+		for (i = 2; i < sizeof(ssid.Ssid); i++)
+			ssid.Ssid[i] = 0x1 + (ssid.Ssid[i] * 0xfe / 0xff);
+		ret = set_essid(usbdev, &ssid);
+	}
+	return ret;
+}
+
+
+static int deauthenticate(struct usbnet *usbdev)
+{
+	int ret;
+
+	ret = disassociate(usbdev, 1);
+	set_default_iw_params(usbdev);
+	return ret;
+}
+
+
+static int add_wep_key(struct usbnet *usbdev, char *key, int keylength,
+    int index)
+{
+	struct NDIS_802_11_WEP *wepkey;
+	int ret;
+
+	/* get memory for max size wep key */
+	wepkey = kmalloc(sizeof(*wepkey) + 13, GFP_KERNEL);
+	if (!wepkey)
+		return -ENOMEM;
+
+	memcpy(wepkey->KeyMaterial, key, keylength);
+	wepkey->Length = cpu_to_le32(sizeof(*wepkey) + keylength);
+	wepkey->KeyIndex = cpu_to_le32(index);
+	if (index == 0)
+		wepkey->KeyIndex = cpu_to_le32(1 << 31);
+	wepkey->KeyLength = cpu_to_le32(keylength);
+	ret = rndis_set_oid(usbdev, OID_802_11_ADD_WEP, wepkey,
+			sizeof(*wepkey) + keylength);
+
+	kfree(wepkey);
+	return ret;
+}
+
+
+/*
+ * wireless extension handlers
+ */
+
+static int rndis_iw_commit(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	/* dummy op */
+	return 0;
+}
+
+
+static int rndis_iw_get_range(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_range *range = (struct iw_range *)extra;
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int len, ret, i, j, num, has_80211g_rates;
+	u8 rates[8];
+	__le32 tx_power;
+
+	devdbg(usbdev, "SIOCGIWRANGE");
+
+	/* clear iw_range struct */
+	memset(range, 0, sizeof(*range));
+	wrqu->data.length = sizeof(*range);
+
+	range->txpower_capa = IW_TXPOW_MWATT;
+	range->num_txpower = 1;
+	if (priv->caps & CAP_SUPPORT_TXPOWER) {
+		len = sizeof(tx_power);
+		ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+						&tx_power, &len);
+		if (ret == 0 && le32_to_cpu(tx_power) != 0xFF)
+			range->txpower[0] = le32_to_cpu(tx_power);
+		else
+			range->txpower[0] = get_bcm4320_power(priv);
+	} else
+		range->txpower[0] = get_bcm4320_power(priv);
+
+	len = sizeof(rates);
+	ret = rndis_query_oid(usbdev, OID_802_11_SUPPORTED_RATES, &rates,
+								&len);
+	has_80211g_rates = 0;
+	if (ret == 0) {
+		j = 0;
+		for (i = 0; i < len; i++) {
+			if (rates[i] == 0)
+				break;
+			range->bitrate[j] = (rates[i] & 0x7f) * 500000;
+			/* check for non 802.11b rates */
+			if (range->bitrate[j] == 6000000 ||
+				range->bitrate[j] == 9000000 ||
+				(range->bitrate[j] >= 12000000 &&
+				range->bitrate[j] != 22000000))
+				has_80211g_rates = 1;
+			j++;
+		}
+		range->num_bitrates = j;
+	} else
+		range->num_bitrates = 0;
+
+	/* fill in 802.11g rates */
+	if (has_80211g_rates) {
+		num = range->num_bitrates;
+		for (i = 0; i < sizeof(rates_80211g); i++) {
+			for (j = 0; j < num; j++) {
+				if (range->bitrate[j] ==
+					rates_80211g[i] * 1000000)
+					break;
+			}
+			if (j == num)
+				range->bitrate[range->num_bitrates++] =
+					rates_80211g[i] * 1000000;
+			if (range->num_bitrates == IW_MAX_BITRATES)
+				break;
+		}
+
+		/* estimated max real througput in bps */
+		range->throughput = 54 * 1000 * 1000 / 2;
+	} else {
+		/* estimated max real througput in bps */
+		range->throughput = 11 * 1000 * 1000 / 2;
+	}
+
+	range->num_channels = (sizeof(freq_chan)/sizeof(freq_chan[0]));
+
+	for (i = 0; i < (sizeof(freq_chan)/sizeof(freq_chan[0])) &&
+			i < IW_MAX_FREQUENCIES; i++) {
+		range->freq[i].i = i + 1;
+		range->freq[i].m = freq_chan[i] * 100000;
+		range->freq[i].e = 1;
+	}
+	range->num_frequency = i;
+
+	range->min_rts = 0;
+	range->max_rts = 2347;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	/* Setup max_qual as ndiswrapper does in iw_ndis.c */
+	range->max_qual.qual = 100;
+	range->max_qual.level = 154;
+	range->max_qual.updated = IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_NOISE_INVALID;
+
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = WIRELESS_EXT;
+
+	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+			IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+	return 0;
+}
+
+
+static int rndis_iw_get_name(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	strcpy(wrqu->name, priv->name);
+	return 0;
+}
+
+
+static int rndis_iw_set_essid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
+{
+	struct NDIS_802_11_SSID ssid;
+	int length = wrqu->essid.length;
+	struct usbnet *usbdev = dev->priv;
+
+	devdbg(usbdev, "SIOCSIWESSID: [flags:%d,len:%d] '%.32s'",
+		wrqu->essid.flags, wrqu->essid.length, essid);
+
+	if (length > NDIS_802_11_LENGTH_SSID)
+		length = NDIS_802_11_LENGTH_SSID;
+
+	ssid.SsidLength = cpu_to_le32(length);
+	if (length > 0) {
+		memcpy(ssid.Ssid, essid, length);
+	} else {
+		memset(ssid.Ssid, 0, NDIS_802_11_LENGTH_SSID);
+	}
+
+	if (!wrqu->essid.flags || length == 0)
+		return disassociate(usbdev, 1);
+	else
+		return set_essid(usbdev, &ssid);
+}
+
+
+static int rndis_iw_get_essid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
+{
+	struct NDIS_802_11_SSID ssid;
+	struct usbnet *usbdev = dev->priv;
+	int ret;
+
+	ret = get_essid(usbdev, &ssid);
+
+	if (ret == 0 && ssid.SsidLength) {
+		wrqu->essid.flags = 1;
+		wrqu->essid.length = le32_to_cpu(ssid.SsidLength);
+		memcpy(essid, ssid.Ssid, wrqu->essid.length);
+		essid[wrqu->essid.length] = 0;
+	} else {
+		memset(essid, 0, sizeof(NDIS_802_11_LENGTH_SSID));
+		wrqu->essid.flags = 0;
+		wrqu->essid.length = 0;
+	}
+	devdbg(usbdev, "SIOCGIWESSID: %s", essid);
+	return ret;
+}
+
+
+static int rndis_iw_get_bssid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	unsigned char bssid[ETH_ALEN];
+	int ret;
+
+	ret = get_bssid(usbdev, bssid);
+
+	if (ret == 0)
+		devdbg(usbdev, "SIOCGIWAP: %02x:%02x:%02x:%02x:%02x:%02x",
+			bssid[0], bssid[1], bssid[2],
+			bssid[3], bssid[4], bssid[5]);
+	else
+		devdbg(usbdev, "SIOCGIWAP: <not associated>");
+
+	wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(wrqu->ap_addr.sa_data, bssid, ETH_ALEN);
+
+	return ret;
+}
+
+
+static int rndis_iw_set_bssid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	u8 *bssid = (u8 *)wrqu->ap_addr.sa_data;
+
+	devdbg(usbdev, "SIOCSIWAP: %02x:%02x:%02x:%02x:%02x:%02x",
+				bssid[0], bssid[1], bssid[2],
+				bssid[3], bssid[4], bssid[5]);
+
+	return rndis_set_oid(usbdev, OID_802_11_BSSID, bssid, ETH_ALEN);
+}
+
+
+static int rndis_iw_set_auth(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_param *p = &wrqu->param;
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int ret = -ENOTSUPP;
+	int auth = Ndis802_11AuthModeOpen;
+	int enc = Ndis802_11EncryptionDisabled;
+	__le32 tmp;
+
+	switch (p->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+		devdbg(usbdev, "SIOCSIWAUTH: WPA_VERSION, %08x", p->value);
+		priv->wpa_version |= p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_CIPHER_PAIRWISE:
+		devdbg(usbdev, "SIOCSIWAUTH: CIPHER_PAIRWISE, %08x", p->value);
+		priv->wpa_cipher_pair = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_CIPHER_GROUP:
+		devdbg(usbdev, "SIOCSIWAUTH: CIPHER_GROUP, %08x", p->value);
+		priv->wpa_cipher_group = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_KEY_MGMT:
+		devdbg(usbdev, "SIOCSIWAUTH: KEY_MGMT, %08x", p->value);
+		priv->wpa_keymgmt = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+		devdbg(usbdev, "SIOCSIWAUTH: TKIP_COUNTERMEASURES, %08x",
+								p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		devdbg(usbdev, "SIOCSIWAUTH: DROP_UNENCRYPTED, %08x", p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		devdbg(usbdev, "SIOCSIWAUTH: 80211_AUTH_ALG, %08x", p->value);
+		priv->wpa_authalg = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		devdbg(usbdev, "SIOCSIWAUTH: WPA_ENABLED, %08x", p->value);
+		if (p->value) {
+			deauthenticate(usbdev);
+			priv->wpa_version &= ~IW_AUTH_WPA_VERSION_DISABLED;
+		} else
+			priv->wpa_version |= IW_AUTH_WPA_VERSION_DISABLED;
+		ret = 0;
+		break;
+
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+		devdbg(usbdev, "SIOCSIWAUTH: RX_UNENCRYPTED_EAPOL, %08x",
+								p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_ROAMING_CONTROL:
+		devdbg(usbdev, "SIOCSIWAUTH: ROAMING_CONTROL, %08x", p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_PRIVACY_INVOKED:
+		devdbg(usbdev, "SIOCSIWAUTH: PRIVACY_INVOKED, %08x", p->value);
+
+		priv->privacy = p->value;
+
+		if (priv->privacy) {
+			switch (priv->wpa_keymgmt) {
+			case IW_AUTH_KEY_MGMT_PSK:
+				if (priv->wpa_version == 0) {
+					auth = Ndis802_11AuthModeShared;
+					enc  = Ndis802_11Encryption1Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA) {
+					auth = Ndis802_11AuthModeWPAPSK;
+					enc  = Ndis802_11Encryption2Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA2) {
+					auth = Ndis802_11AuthModeWPA2PSK;
+					enc  = Ndis802_11Encryption3Enabled;
+				} else {
+					devdbg(usbdev, "weird wpa_version %08x",
+							priv->wpa_version);
+					auth = Ndis802_11AuthModeWPA2PSK;
+					enc  = Ndis802_11Encryption3Enabled;
+				}
+				break;
+
+			case IW_AUTH_KEY_MGMT_802_1X:
+				if (priv->wpa_version == 0) {
+					auth = Ndis802_11AuthModeShared;
+					enc  = Ndis802_11Encryption1Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA) {
+					auth = Ndis802_11AuthModeWPA;
+					enc  = Ndis802_11Encryption2Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA2) {
+					auth = Ndis802_11AuthModeWPA2;
+					enc  = Ndis802_11Encryption3Enabled;
+				} else {
+					devdbg(usbdev, "weird wpa_version %08x",
+							priv->wpa_version);
+					auth = Ndis802_11AuthModeWPA2;
+					enc  = Ndis802_11Encryption3Enabled;
+				}
+				break;
+			}
+		} else {
+			auth = Ndis802_11AuthModeOpen;
+			enc  = Ndis802_11EncryptionDisabled;
+		}
+		tmp = cpu_to_le32(auth);
+		rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp,
+								sizeof(tmp));
+		tmp = cpu_to_le32(enc);
+		rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp,
+								sizeof(tmp));
+		ret = 0;
+		break;
+
+	default:
+		devdbg(usbdev, "SIOCSIWAUTH: UKNOWN  %08x, %08x",
+			p->flags & IW_AUTH_INDEX, p->value);
+	}
+	return ret;
+}
+
+
+static int rndis_iw_get_mode(struct net_device *dev,
+				struct iw_request_info *info,
+				union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	switch (priv->mode) {
+	case Ndis802_11IBSS:
+		wrqu->mode = IW_MODE_ADHOC;
+		break;
+	case Ndis802_11Infrastructure:
+		wrqu->mode = IW_MODE_INFRA;
+		break;
+	/*case Ndis802_11AutoUnknown:*/
+	default:
+		wrqu->mode = IW_MODE_AUTO;
+		break;
+	}
+	devdbg(usbdev, "SIOCGIWMODE: %08x", wrqu->mode);
+	return 0;
+}
+
+
+static int rndis_iw_set_mode(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int i, ret = -EINVAL;
+	__le32 tmp;
+
+	devdbg(usbdev, "SIOCSIWMODE: %08x", wrqu->mode);
+
+	switch (wrqu->mode) {
+	case IW_MODE_ADHOC:
+		priv->mode = Ndis802_11IBSS;
+		break;
+
+	case IW_MODE_INFRA:
+		priv->mode = Ndis802_11Infrastructure;
+		break;
+
+	/*case IW_MODE_AUTO:*/
+	default:
+		priv->mode = Ndis802_11AutoUnknown;
+		break;
+	}
+	tmp = cpu_to_le32(priv->mode);
+	ret = rndis_set_oid(usbdev, OID_802_11_INFRASTRUCTURE_MODE, &tmp,
+								sizeof(tmp));
+	if (ret != 0)
+		return ret;
+
+	/* NDIS drivers clear keys when infrastructure mode is
+	 * changed. But Linux tools assume otherwise. So set the
+	 * keys */
+	if (priv->wpa_keymgmt == 0 ||
+		priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) {
+		for (i = 0; i < 4; i++) {
+			if (priv->wep_key_len[i] > 0)
+				add_wep_key(usbdev, priv->wep_keys[i],
+						priv->wep_key_len[i], i);
+		}
+	}
+
+	return ret;
+}
+
+
+static int rndis_iw_set_encode(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_point *param = &wrqu->encoding;
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int	ret = 0;
+	int	index;
+	int	auth =  Ndis802_11AuthModeOpen;
+	int	enc = Ndis802_11EncryptionDisabled;
+	__le32	tmp;
+
+	devdbg(usbdev, "SIOCSIWENCODE: %08x, %08x",
+		param->flags, param->length);
+
+	index = (param->flags & IW_ENCODE_INDEX) - 1;
+
+	if (param->length > 0) {
+		int current_index = priv->wep_def_key;
+		/* Check the size of the key */
+		if (param->length > 13)
+			return -EINVAL;
+		/* Check the index (none -> use current) */
+		if (index < 0 || index >= 4)
+			index = current_index;
+		else
+			priv->wep_def_key = index;
+		/* Set the length */
+		if (param->length > 5)
+			priv->wep_key_len[index] = 13;
+		else
+			if (param->length > 0)
+				priv->wep_key_len[index] = 5;
+			else
+				/* Disable the key */
+				priv->wep_key_len[index] = 0;
+		/* Check if the key is not marked as invalid */
+		if (!(param->flags & IW_ENCODE_NOKEY)) {
+			/* Cleanup */
+			memset(priv->wep_keys[index], 0, 13);
+			/* Copy the key in the driver */
+			memcpy(priv->wep_keys[index], extra, param->length);
+		}
+		/* WE specify that if a valid key is set, encryption
+		 * should be enabled (user may turn it off later)
+		 * This is also how "iwconfig ethX key on" works
+		 */
+		if (index == current_index && priv->wep_key_len[index] > 0)
+			priv->wep_enabled = 1;
+	} else {
+		if (index >= 0 && index < 4)
+			priv->wep_def_key = index;
+		else
+			/* Don't complain if only change the mode */
+			if (!param->flags & IW_ENCODE_MODE)
+				return -EINVAL;
+	}
+
+	if (index >= 0 && index < 4) {
+		add_wep_key(usbdev, priv->wep_keys[index],
+				priv->wep_key_len[index], index);
+	}
+
+
+	/* Read the flags */
+	if (param->flags & IW_ENCODE_DISABLED) {
+		priv->wep_enabled = 0;
+	} else {
+		priv->wep_enabled = 1;
+	}
+
+	if (param->flags & IW_ENCODE_RESTRICTED)
+		auth = Ndis802_11AuthModeShared;
+	if (param->flags & IW_ENCODE_OPEN)
+		auth = Ndis802_11AuthModeOpen;
+
+	if (priv->wep_enabled) {
+		if (param->flags & IW_ENCODE_MODE)
+			enc = Ndis802_11WEPEnabled;
+	} else
+		enc = Ndis802_11WEPDisabled;
+
+	tmp = cpu_to_le32(auth);
+	rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp,
+			sizeof(tmp));
+	tmp = cpu_to_le32(enc);
+	rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp,
+			sizeof(tmp));
+
+	return ret;
+}
+
+
+static int rndis_iw_set_encode_ext(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_encode_ext *ext = (void *)extra;
+	struct usbnet *usbdev = dev->priv;
+	int ret = -EINVAL, keyindex;
+	struct NDIS_802_11_REMOVE_KEY rem_key;
+	struct NDIS_802_11_KEY add_key;
+
+	devdbg(usbdev, "SIOCSIWENCODEEXT: ext. flags:%08x, alg:%08x: keylen:%d",
+				ext->ext_flags, ext->alg, ext->key_len);
+	devdbg(usbdev, "                : enc. flags:%08x, length:%d",
+				wrqu->encoding.flags, wrqu->encoding.length);
+
+	keyindex = (wrqu->encoding.flags & IW_ENCODE_INDEX) - 1;
+
+	/* TX key */
+	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+		keyindex |= (1 << 31);
+
+	/* pairwise key */
+	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY))
+		keyindex |= (1 << 30);
+
+	if (wrqu->encoding.flags & IW_ENCODE_DISABLED) {
+		memset(&rem_key, 0, sizeof(rem_key));
+		rem_key.Length = cpu_to_le32(sizeof(rem_key));
+		rem_key.KeyIndex = cpu_to_le32(keyindex);
+		if (keyindex & (1 << 30)) {
+			memcpy(rem_key.Bssid, ext->addr.sa_data, ETH_ALEN);
+		} else {
+			memset(rem_key.Bssid, 0xff, ETH_ALEN);
+		}
+		ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_KEY, &rem_key,
+							sizeof(rem_key));
+	} else {
+		if (ext->key_len > sizeof(add_key.KeyMaterial))
+			return -EINVAL;
+
+		memset(&add_key, 0, sizeof(add_key));
+		add_key.Length = cpu_to_le32(sizeof(add_key));
+
+		if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+			memcpy(&add_key.KeyRSC, ext->rx_seq,
+					IW_ENCODE_SEQ_MAX_SIZE);
+			keyindex |= (1 << 29);
+		}
+
+		if (ext->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID) {
+			memcpy(&add_key.KeyRSC, ext->tx_seq,
+					IW_ENCODE_SEQ_MAX_SIZE);
+			keyindex |= (1 << 29);
+		}
+
+		add_key.KeyIndex = cpu_to_le32(keyindex);
+		add_key.KeyLength = cpu_to_le32(ext->key_len);
+		if (keyindex & (1 << 30)) {
+			memcpy(add_key.Bssid, ext->addr.sa_data, ETH_ALEN);
+		} else {
+			memset(add_key.Bssid, 0xff, ETH_ALEN);
+		}
+		if (ext->alg == IW_ENCODE_ALG_TKIP) {
+			memcpy(add_key.KeyMaterial, ext->key, 16);
+			memcpy(add_key.KeyMaterial + 16, ext->key + 24, 8);
+			memcpy(add_key.KeyMaterial + 24, ext->key + 16, 8);
+		} else {
+			memcpy(add_key.KeyMaterial, ext->key, ext->key_len);
+		}
+		/* kludge alert - breaking a race between ioctl and network
+		 * packet.  This delay is to prevent encryption being enabled
+		 * while the last authentication handshake packet is still
+		 * underway in the stack somewhere. If encryption is enabled
+		 * too early the AP will not see this last handshake packet and
+		 * association fails.
+		 *
+		 * also see:
+		 * http://lists.shmoo.com/pipermail/hostap/2006-September/\
+		 *						014157.html
+		 */
+		msleep(500);
+
+		ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &add_key,
+							sizeof(add_key));
+	}
+	return ret;
+}
+
+
+static int rndis_iw_set_scan(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_param *param = &wrqu->param;
+	struct usbnet *usbdev = dev->priv;
+	union iwreq_data evt;
+	int ret = -EINVAL;
+	__le32 tmp;
+
+	devdbg(usbdev, "SIOCSIWSCAN");
+
+	if (param->flags == 0) {
+		tmp = ccpu2(1);
+		ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
+								sizeof(tmp));
+		evt.data.flags = 0;
+		evt.data.length = 0;
+		wireless_send_event(dev, SIOCGIWSCAN, &evt, NULL);
+	}
+	return ret;
+}
+
+
+static char *rndis_translate_scan(struct net_device *dev,
+    char *cev, char *end_buf, struct NDIS_WLAN_BSSID_EX *bssid)
+{
+#ifdef DEBUG
+	struct usbnet *usbdev = dev->priv;
+#endif
+	struct ieee80211_info_element *ie;
+	char *current_val;
+	int bssid_len, ie_len, i;
+	u32 beacon, atim;
+	struct iw_event iwe;
+	unsigned char sbuf[32];
+
+	bssid_len = le32_to_cpu(bssid->Length);
+
+	devdbg(usbdev, "BSSID %02x:%02x:%02x:%02x:%02x:%02x",
+			bssid->MacAddress[0], bssid->MacAddress[1],
+			bssid->MacAddress[2], bssid->MacAddress[3],
+			bssid->MacAddress[4], bssid->MacAddress[5]);
+	iwe.cmd = SIOCGIWAP;
+	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(iwe.u.ap_addr.sa_data, bssid->MacAddress, ETH_ALEN);
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+	devdbg(usbdev, "SSID(%d) %s",
+		le32_to_cpu(bssid->Ssid.SsidLength),
+		bssid->Ssid.Ssid);
+	iwe.cmd = SIOCGIWESSID;
+	iwe.u.essid.length = le32_to_cpu(bssid->Ssid.SsidLength);
+	iwe.u.essid.flags = 1;
+	cev = iwe_stream_add_point(cev, end_buf, &iwe,
+						bssid->Ssid.Ssid);
+
+	devdbg(usbdev, "MODE %d",
+			le32_to_cpu(bssid->InfrastructureMode));
+	iwe.cmd = SIOCGIWMODE;
+	switch (le32_to_cpu(bssid->InfrastructureMode)) {
+	case Ndis802_11IBSS:
+		iwe.u.mode = IW_MODE_ADHOC;
+		break;
+	case Ndis802_11Infrastructure:
+		iwe.u.mode = IW_MODE_INFRA;
+		break;
+	/*case Ndis802_11AutoUnknown:*/
+	default:
+		iwe.u.mode = IW_MODE_AUTO;
+		break;
+	}
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN);
+
+	devdbg(usbdev, "FREQ %d kHz",
+		le32_to_cpu(bssid->Configuration.DSConfig));
+	iwe.cmd = SIOCGIWFREQ;
+	dsconfig_to_freq(le32_to_cpu(bssid->Configuration.DSConfig),
+								&iwe.u.freq);
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN);
+
+	devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->Rssi));
+	iwe.cmd = IWEVQUAL;
+	iwe.u.qual.qual  = level_to_qual(le32_to_cpu(bssid->Rssi));
+	iwe.u.qual.level = le32_to_cpu(bssid->Rssi);
+	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
+			| IW_QUAL_LEVEL_UPDATED
+			| IW_QUAL_NOISE_INVALID;
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN);
+
+	devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->Privacy));
+	iwe.cmd = SIOCGIWENCODE;
+	iwe.u.data.length = 0;
+	if (le32_to_cpu(bssid->Privacy) == Ndis802_11PrivFilterAcceptAll)
+		iwe.u.data.flags = IW_ENCODE_DISABLED;
+	else
+		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+
+	cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL);
+
+	devdbg(usbdev, "RATES:");
+	current_val = cev + IW_EV_LCP_LEN;
+	iwe.cmd = SIOCGIWRATE;
+	for (i = 0; i < sizeof(bssid->SupportedRates); i++) {
+		if (bssid->SupportedRates[i] & 0x7f) {
+			iwe.u.bitrate.value =
+				((bssid->SupportedRates[i] & 0x7f) *
+				500000);
+			devdbg(usbdev, " %d", iwe.u.bitrate.value);
+			current_val = iwe_stream_add_value(cev,
+				current_val, end_buf, &iwe,
+				IW_EV_PARAM_LEN);
+		}
+	}
+
+	if ((current_val - cev) > IW_EV_LCP_LEN)
+		cev = current_val;
+
+	beacon = le32_to_cpu(bssid->Configuration.BeaconPeriod);
+	devdbg(usbdev, "BCN_INT %d", beacon);
+	iwe.cmd = IWEVCUSTOM;
+	snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon);
+	iwe.u.data.length = strlen(sbuf);
+	cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf);
+
+	atim = le32_to_cpu(bssid->Configuration.ATIMWindow);
+	devdbg(usbdev, "ATIM %d", atim);
+	iwe.cmd = IWEVCUSTOM;
+	snprintf(sbuf, sizeof(sbuf), "atim=%u", atim);
+	iwe.u.data.length = strlen(sbuf);
+	cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf);
+
+	ie = (void *)(bssid->IEs + sizeof(struct NDIS_802_11_FIXED_IEs));
+	ie_len = min(bssid_len - (int)sizeof(*bssid),
+					(int)le32_to_cpu(bssid->IELength));
+	ie_len -= sizeof(struct NDIS_802_11_FIXED_IEs);
+	while (ie_len >= sizeof(*ie) && sizeof(*ie) + ie->len <= ie_len) {
+		if ((ie->id == MFIE_TYPE_GENERIC && ie->len >= 4 &&
+				memcmp(ie->data, "\x00\x50\xf2\x01", 4) == 0) ||
+				ie->id == MFIE_TYPE_RSN) {
+			devdbg(usbdev, "IE: WPA%d",
+					(ie->id == MFIE_TYPE_RSN) ? 2 : 1);
+			iwe.cmd = IWEVGENIE;
+			iwe.u.data.length = min(ie->len + 2, MAX_WPA_IE_LEN);
+			cev = iwe_stream_add_point(cev, end_buf, &iwe,
+								   (u8 *)ie);
+		}
+
+		ie_len -= sizeof(*ie) + ie->len;
+		ie = (struct ieee80211_info_element *)&ie->data[ie->len];
+	}
+
+	return cev;
+}
+
+
+static int rndis_iw_get_scan(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	void *buf = NULL;
+	char *cev = extra;
+	struct NDIS_802_11_BSSID_LIST_EX *bssid_list;
+	struct NDIS_WLAN_BSSID_EX *bssid;
+	int ret = -EINVAL, len, count, bssid_len;
+
+	devdbg(usbdev, "SIOCGIWSCAN");
+
+	len = CONTROL_BUFFER_SIZE;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
+
+	if (ret != 0)
+		goto out;
+
+	bssid_list = buf;
+	bssid = bssid_list->Bssid;
+	bssid_len = le32_to_cpu(bssid->Length);
+	count = le32_to_cpu(bssid_list->NumberOfItems);
+	devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count);
+
+	while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
+		cev = rndis_translate_scan(dev, cev, extra + IW_SCAN_MAX_DATA,
+									bssid);
+		bssid = (void *)bssid + bssid_len;
+		bssid_len = le32_to_cpu(bssid->Length);
+		count--;
+	}
+
+out:
+	wrqu->data.length = cev - extra;
+	wrqu->data.flags = 0;
+	kfree(buf);
+	return ret;
+}
+
+
+static int rndis_iw_set_genie(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int ret = 0;
+
+#ifdef DEBUG
+	int j;
+	u8 *gie = extra;
+	for (j = 0; j < wrqu->data.length; j += 8)
+		devdbg(usbdev,
+			"SIOCSIWGENIE %04x - "
+			"%02x %02x %02x %02x %02x %02x %02x %02x", j,
+			gie[j + 0], gie[j + 1], gie[j + 2], gie[j + 3],
+			gie[j + 4], gie[j + 5], gie[j + 6], gie[j + 7]);
+#endif
+	/* clear existing IEs */
+	if (priv->wpa_ie_len) {
+		kfree(priv->wpa_ie);
+		priv->wpa_ie_len = 0;
+	}
+
+	/* set new IEs */
+	priv->wpa_ie = kmalloc(wrqu->data.length, GFP_KERNEL);
+	if (priv->wpa_ie) {
+		priv->wpa_ie_len = wrqu->data.length;
+		memcpy(priv->wpa_ie, extra, priv->wpa_ie_len);
+	} else
+		ret = -ENOMEM;
+	return ret;
+}
+
+
+static int rndis_iw_get_genie(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	devdbg(usbdev, "SIOCGIWGENIE");
+
+	if (priv->wpa_ie_len == 0 || priv->wpa_ie == NULL) {
+		wrqu->data.length = 0;
+		return 0;
+	}
+
+	if (wrqu->data.length < priv->wpa_ie_len)
+		return -E2BIG;
+
+	wrqu->data.length = priv->wpa_ie_len;
+	memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
+
+	return 0;
+}
+
+
+static int rndis_iw_set_rts(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	devdbg(usbdev, "SIOCSIWRTS");
+
+	tmp = cpu_to_le32(wrqu->rts.value);
+	return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp,
+								sizeof(tmp));
+}
+
+
+static int rndis_iw_get_rts(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	int len, ret;
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, &len);
+	if (ret == 0) {
+		wrqu->rts.value = le32_to_cpu(tmp);
+		wrqu->rts.flags = 1;
+		wrqu->rts.disabled = 0;
+	}
+
+	devdbg(usbdev, "SIOCGIWRTS: %d", wrqu->rts.value);
+
+	return ret;
+}
+
+
+static int rndis_iw_set_frag(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+
+	devdbg(usbdev, "SIOCSIWFRAG");
+
+	tmp = cpu_to_le32(wrqu->frag.value);
+	return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
+								sizeof(tmp));
+}
+
+
+static int rndis_iw_get_frag(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	int len, ret;
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
+									&len);
+	if (ret == 0) {
+		wrqu->frag.value = le32_to_cpu(tmp);
+		wrqu->frag.flags = 1;
+		wrqu->frag.disabled = 0;
+	}
+	devdbg(usbdev, "SIOCGIWFRAG: %d", wrqu->frag.value);
+	return ret;
+}
+
+
+static int rndis_iw_set_nick(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	devdbg(usbdev, "SIOCSIWNICK");
+
+	priv->nick_len = wrqu->data.length;
+	if (priv->nick_len > 32)
+		priv->nick_len = 32;
+
+	memcpy(priv->nick, extra, priv->nick_len);
+	return 0;
+}
+
+
+static int rndis_iw_get_nick(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	wrqu->data.flags = 1;
+	wrqu->data.length = priv->nick_len;
+	memcpy(extra, priv->nick, priv->nick_len);
+
+	devdbg(usbdev, "SIOCGIWNICK: '%s'", priv->nick);
+
+	return 0;
+}
+
+
+static int rndis_iw_set_freq(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct NDIS_802_11_CONFIGURATION config;
+	unsigned int dsconfig;
+	int len, ret;
+
+	/* this OID is valid only when not associated */
+	if (is_associated(usbdev))
+		return 0;
+
+	dsconfig = 0;
+	if (freq_to_dsconfig(&wrqu->freq, &dsconfig))
+		return -EINVAL;
+
+	len = sizeof(config);
+	ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
+	if (ret != 0) {
+		devdbg(usbdev, "SIOCSIWFREQ: querying configuration failed");
+		return 0;
+	}
+
+	config.DSConfig = cpu_to_le32(dsconfig);
+
+	devdbg(usbdev, "SIOCSIWFREQ: %d * 10^%d", wrqu->freq.m, wrqu->freq.e);
+	return rndis_set_oid(usbdev, OID_802_11_CONFIGURATION, &config,
+								sizeof(config));
+}
+
+
+static int rndis_iw_get_freq(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct NDIS_802_11_CONFIGURATION config;
+	int len, ret;
+
+	len = sizeof(config);
+	ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
+	if (ret == 0)
+		dsconfig_to_freq(le32_to_cpu(config.DSConfig), &wrqu->freq);
+
+	devdbg(usbdev, "SIOCGIWFREQ: %d", wrqu->freq.m);
+	return ret;
+}
+
+
+static int rndis_iw_get_txpower(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	__le32 tx_power;
+	int ret = 0, len;
+
+	if (priv->radio_on) {
+		if (priv->caps & CAP_SUPPORT_TXPOWER) {
+			len = sizeof(tx_power);
+			ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+							&tx_power, &len);
+			if (ret != 0)
+				return ret;
+		} else
+			/* fake incase not supported */
+			tx_power = cpu_to_le32(get_bcm4320_power(priv));
+
+		wrqu->txpower.flags = IW_TXPOW_MWATT;
+		wrqu->txpower.value = le32_to_cpu(tx_power);
+		wrqu->txpower.disabled = 0;
+	} else {
+		wrqu->txpower.flags = IW_TXPOW_MWATT;
+		wrqu->txpower.value = 0;
+		wrqu->txpower.disabled = 1;
+	}
+
+	devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value);
+
+	return ret;
+}
+
+
+static int rndis_iw_set_txpower(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	__le32 tx_power;
+	int ret = 0;
+
+	if (wrqu->txpower.disabled)
+		tx_power = 0;
+	else {
+		if (wrqu->txpower.flags == IW_TXPOW_MWATT)
+			tx_power = cpu_to_le32(wrqu->txpower.value);
+		else {
+			/* wrqu->txpower.flags == IW_TXPOW_DBM */
+			if (wrqu->txpower.value > 20)
+				tx_power = cpu_to_le32(128);
+			else if (wrqu->txpower.value < -43)
+				tx_power = cpu_to_le32(127);
+			else {
+				signed char tmp;
+				tmp = wrqu->txpower.value;
+				tmp = -12 - tmp;
+				tmp <<= 2;
+				tx_power = cpu_to_le32((unsigned char)tmp);
+			}
+		}
+	}
+
+	devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power));
+
+	if (le32_to_cpu(tx_power) != 0) {
+		if (priv->caps & CAP_SUPPORT_TXPOWER) {
+			/* turn radio on first */
+			if (!priv->radio_on)
+				disassociate(usbdev, 1);
+
+			ret = rndis_set_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+						&tx_power, sizeof(tx_power));
+			if (ret != 0)
+				ret = -EOPNOTSUPP;
+			return ret;
+		} else {
+			/* txpower unsupported, just turn radio on */
+			if (!priv->radio_on)
+				return disassociate(usbdev, 1);
+			return 0; /* all ready on */
+		}
+	}
+
+	/* tx_power == 0, turn off radio */
+	return disassociate(usbdev, 0);
+}
+
+
+static int rndis_iw_get_rate(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	int ret, len;
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &tmp, &len);
+	if (ret == 0) {
+		wrqu->bitrate.value = le32_to_cpu(tmp) * 100;
+		wrqu->bitrate.disabled = 0;
+		wrqu->bitrate.flags = 1;
+	}
+	return ret;
+}
+
+
+static int rndis_iw_set_mlme(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct iw_mlme *mlme = (struct iw_mlme *)extra;
+	unsigned char bssid[ETH_ALEN];
+
+	get_bssid(usbdev, bssid);
+
+	if (memcmp(bssid, mlme->addr.sa_data, ETH_ALEN))
+		return -EINVAL;
+
+	switch (mlme->cmd) {
+	case IW_MLME_DEAUTH:
+		return deauthenticate(usbdev);
+	case IW_MLME_DISASSOC:
+		return disassociate(usbdev, 1);
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+
+static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	mutex_lock(&priv->stats_lock);
+	memcpy(&priv->iwstats, &priv->privstats, sizeof(priv->iwstats));
+	mutex_unlock(&priv->stats_lock);
+
+	return &priv->iwstats;
+}
+
+
+#define IW_IOCTL(x) [(x) - SIOCSIWCOMMIT]
+static const iw_handler rndis_iw_handler[] =
+{
+	IW_IOCTL(SIOCSIWCOMMIT)    = rndis_iw_commit,
+	IW_IOCTL(SIOCGIWNAME)      = rndis_iw_get_name,
+	IW_IOCTL(SIOCSIWFREQ)      = rndis_iw_set_freq,
+	IW_IOCTL(SIOCGIWFREQ)      = rndis_iw_get_freq,
+	IW_IOCTL(SIOCSIWMODE)      = rndis_iw_set_mode,
+	IW_IOCTL(SIOCGIWMODE)      = rndis_iw_get_mode,
+	IW_IOCTL(SIOCGIWRANGE)     = rndis_iw_get_range,
+	IW_IOCTL(SIOCSIWAP)        = rndis_iw_set_bssid,
+	IW_IOCTL(SIOCGIWAP)        = rndis_iw_get_bssid,
+	IW_IOCTL(SIOCSIWSCAN)      = rndis_iw_set_scan,
+	IW_IOCTL(SIOCGIWSCAN)      = rndis_iw_get_scan,
+	IW_IOCTL(SIOCSIWESSID)     = rndis_iw_set_essid,
+	IW_IOCTL(SIOCGIWESSID)     = rndis_iw_get_essid,
+	IW_IOCTL(SIOCSIWNICKN)     = rndis_iw_set_nick,
+	IW_IOCTL(SIOCGIWNICKN)     = rndis_iw_get_nick,
+	IW_IOCTL(SIOCGIWRATE)      = rndis_iw_get_rate,
+	IW_IOCTL(SIOCSIWRTS)       = rndis_iw_set_rts,
+	IW_IOCTL(SIOCGIWRTS)       = rndis_iw_get_rts,
+	IW_IOCTL(SIOCSIWFRAG)      = rndis_iw_set_frag,
+	IW_IOCTL(SIOCGIWFRAG)      = rndis_iw_get_frag,
+	IW_IOCTL(SIOCSIWTXPOW)     = rndis_iw_set_txpower,
+	IW_IOCTL(SIOCGIWTXPOW)     = rndis_iw_get_txpower,
+	IW_IOCTL(SIOCSIWENCODE)    = rndis_iw_set_encode,
+	IW_IOCTL(SIOCSIWENCODEEXT) = rndis_iw_set_encode_ext,
+	IW_IOCTL(SIOCSIWAUTH)      = rndis_iw_set_auth,
+	IW_IOCTL(SIOCSIWGENIE)     = rndis_iw_set_genie,
+	IW_IOCTL(SIOCGIWGENIE)     = rndis_iw_get_genie,
+	IW_IOCTL(SIOCSIWMLME)      = rndis_iw_set_mlme,
+};
+
+static const iw_handler rndis_wext_private_handler[] = {
+};
+
+static const struct iw_priv_args rndis_wext_private_args[] = {
+};
+
+
+static const struct iw_handler_def rndis_iw_handlers = {
+	.num_standard = ARRAY_SIZE(rndis_iw_handler),
+	.num_private  = ARRAY_SIZE(rndis_wext_private_handler),
+	.num_private_args = ARRAY_SIZE(rndis_wext_private_args),
+	.standard = (iw_handler *)rndis_iw_handler,
+	.private  = (iw_handler *)rndis_wext_private_handler,
+	.private_args = (struct iw_priv_args *)rndis_wext_private_args,
+	.get_wireless_stats = rndis_get_wireless_stats,
+};
+
+
+static void rndis_wext_connect_event_work(struct work_struct *work)
+{
+	struct rndis_wext_private *priv =
+		container_of(work, struct rndis_wext_private, work);
+	struct usbnet *usbdev = priv->usbdev;
+	union iwreq_data evt;
+	unsigned char bssid[ETH_ALEN];
+	int ret;
+
+	ret = get_bssid(usbdev, bssid);
+
+	if (!ret) {
+		evt.data.flags = 0;
+		evt.data.length = 0;
+		memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN);
+		wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL);
+	}
+}
+
+static void rndis_wext_link_change(struct usbnet *dev, int state)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	union iwreq_data evt;
+
+	if (state) {
+		/* queue work to avoid recursive calls into rndis_command */
+		queue_work(priv->workqueue, &priv->work);
+	} else {
+		evt.data.flags = 0;
+		evt.data.length = 0;
+		memset(evt.ap_addr.sa_data, 0, ETH_ALEN);
+		wireless_send_event(dev->net, SIOCGIWAP, &evt, NULL);
+	}
+}
+
+static int rndis_wext_get_caps(struct usbnet *dev)
+{
+	struct {
+		__le32	num_items;
+		__le32	items[8];
+	} networks_supported;
+	int len, retval, i, n;
+	__le32 tx_power;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+
+	/* determine if supports setting txpower */
+	len = sizeof(tx_power);
+	retval = rndis_query_oid(dev, OID_802_11_TX_POWER_LEVEL, &tx_power,
+								&len);
+	if (retval == 0 && le32_to_cpu(tx_power) != 0xFF)
+		priv->caps |= CAP_SUPPORT_TXPOWER;
+
+	/* determine supported modes */
+	len = sizeof(networks_supported);
+	retval = rndis_query_oid(dev, OID_802_11_NETWORK_TYPES_SUPPORTED,
+						&networks_supported, &len);
+	if (retval >= 0) {
+		n = le32_to_cpu(networks_supported.num_items);
+		if (n > 8)
+			n = 8;
+		for (i = 0; i < n; i++) {
+			switch (le32_to_cpu(networks_supported.items[i])) {
+			case Ndis802_11FH:
+			case Ndis802_11DS:
+				priv->caps |= CAP_MODE_80211B;
+				break;
+			case Ndis802_11OFDM5:
+				priv->caps |= CAP_MODE_80211A;
+				break;
+			case Ndis802_11OFDM24:
+				priv->caps |= CAP_MODE_80211G;
+				break;
+			}
+		}
+		if (priv->caps & CAP_MODE_80211A)
+			strcat(priv->name, "a");
+		if (priv->caps & CAP_MODE_80211B)
+			strcat(priv->name, "b");
+		if (priv->caps & CAP_MODE_80211G)
+			strcat(priv->name, "g");
+	}
+
+	return retval;
+}
+
+
+#define STATS_UPDATE_JIFFIES (HZ * 2)
+static void rndis_update_wireless_stats(struct work_struct *work)
+{
+	struct rndis_wext_private *priv =
+		container_of(work, struct rndis_wext_private, stats_work.work);
+	struct usbnet *usbdev = priv->usbdev;
+	struct NDIS_802_11_STATS stats;
+	struct iw_statistics iwstats;
+	__le32 rssi;
+	int len, ret;
+
+	mutex_lock(&priv->stats_lock);
+	memcpy(&iwstats, &priv->privstats, sizeof(iwstats));
+	mutex_unlock(&priv->stats_lock);
+
+	/* only update stats when connected */
+	if (!is_associated(usbdev)) {
+		iwstats.qual.qual = 0;
+		iwstats.qual.level = 0;
+		iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_NOISE_INVALID
+				| IW_QUAL_QUAL_INVALID
+				| IW_QUAL_LEVEL_INVALID;
+		goto end;
+	}
+
+	len = sizeof(rssi);
+	ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len);
+
+	devdbg(usbdev, "stats: OID_802_11_RSSI -> %d, rssi:%d", ret,
+							le32_to_cpu(rssi));
+	if (ret == 0) {
+		memset(&iwstats.qual, 0, sizeof(iwstats.qual));
+		iwstats.qual.qual  = level_to_qual(le32_to_cpu(rssi));
+		iwstats.qual.level = le32_to_cpu(rssi);
+		iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_NOISE_INVALID;
+	}
+
+	len = sizeof(stats);
+	ret = rndis_query_oid(usbdev, OID_802_11_STATISTICS, &stats, &len);
+
+	devdbg(usbdev, "stats: OID_802_11_STATISTICS -> %d", ret);
+	if (ret == 0) {
+		memset(&iwstats.discard, 0, sizeof(iwstats.discard));
+		iwstats.discard.retries =
+			(u32)le64_to_cpu(stats.Retry) +
+			(u32)le64_to_cpu(stats.MultiRetry);
+		iwstats.discard.misc =
+			(u32)le64_to_cpu(stats.FcsErr) +
+			(u32)le64_to_cpu(stats.RtssFail) +
+			(u32)le64_to_cpu(stats.AckFail) +
+			(u32)le64_to_cpu(stats.FrameDup);
+		iwstats.discard.fragment =
+			(u32)le64_to_cpu(stats.RxFrag) +
+			(u32)le64_to_cpu(stats.RxMultiFrag);
+	}
+
+end:
+	mutex_lock(&priv->stats_lock);
+	memcpy(&priv->privstats, &iwstats, sizeof(iwstats));
+	mutex_unlock(&priv->stats_lock);
+
+	queue_delayed_work(priv->workqueue, &priv->stats_work,
+		round_jiffies_relative(STATS_UPDATE_JIFFIES));
+
+}
+
+static int rndis_wext_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct net_device *net = dev->net;
+	struct rndis_wext_private *priv;
+
+	/* allocate rndis private data */
+	priv = kmalloc(sizeof(struct rndis_wext_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev->driver_priv = priv;
+	memset(priv, 0, sizeof(*priv));
+	memset(priv->name, 0, sizeof(priv->name));
+	strcpy(priv->name, "IEEE802.11");
+	net->wireless_handlers = &rndis_iw_handlers;
+	priv->usbdev = dev;
+
+	mutex_init(&priv->command_lock);
+	mutex_init(&priv->stats_lock);
+
+	priv->iwstats.qual.qual = 0;
+	priv->iwstats.qual.level = 0;
+	priv->iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+					| IW_QUAL_LEVEL_UPDATED
+					| IW_QUAL_NOISE_INVALID
+					| IW_QUAL_QUAL_INVALID
+					| IW_QUAL_LEVEL_INVALID;
+
+	rndis_wext_get_caps(dev);
+	set_default_iw_params(dev);
+
+	/* turn radio on */
+	priv->radio_on = 1;
+	disassociate(dev, 1);
+
+	/* because rndis_command() sleeps we need to use workqueue */
+	priv->workqueue = create_singlethread_workqueue("rndis_wext");
+	INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
+	queue_delayed_work(priv->workqueue, &priv->stats_work,
+		round_jiffies_relative(STATS_UPDATE_JIFFIES));
+	INIT_WORK(&priv->work, rndis_wext_connect_event_work);
+
+	return 0;
+}
+
+static void rndis_wext_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+
+	/* turn radio off */
+	disassociate(dev, 0);
+
+	cancel_delayed_work_sync(&priv->stats_work);
+	cancel_work_sync(&priv->work);
+	flush_workqueue(priv->workqueue);
+	destroy_workqueue(priv->workqueue);
+
+	if (priv && priv->wpa_ie_len)
+		kfree(priv->wpa_ie);
+	kfree(priv);
+
+	rndis_unbind(dev, intf);
+}
+
+static int rndis_wext_reset(struct usbnet *dev)
+{
+	return deauthenticate(dev);
+}
+
+struct driver_info rndis_wext_info = {
+	.description =	"Wireless RNDIS device",
+	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.bind =		rndis_wext_bind,
+	.unbind =	rndis_wext_unbind,
+	.status =	rndis_status,
+	.rx_fixup =	rndis_rx_fixup,
+	.tx_fixup =	rndis_tx_fixup,
+	.reset =	rndis_wext_reset,
+	.link_change =	rndis_wext_link_change,
+};
diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
index 1fae434..83860a0 100644
--- a/drivers/net/usb/usbnet.h
+++ b/drivers/net/usb/usbnet.h
@@ -31,6 +31,7 @@ struct usbnet {
 	struct usb_interface	*intf;
 	struct driver_info	*driver_info;
 	const char		*driver_name;
+	void			*driver_priv;
 	wait_queue_head_t	*wait;
 	struct mutex		phy_mutex;
 	unsigned char		suspend_count;
@@ -109,6 +110,9 @@ struct driver_info {
 	/* fixup rx packet (strip framing) */
 	int	(*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
 
+	/* called when link state changes */
+	void    (*link_change)(struct usbnet *dev, int state);
+
 	/* fixup tx packet (add framing) */
 	struct sk_buff	*(*tx_fixup)(struct usbnet *dev,
 				struct sk_buff *skb, gfp_t flags);
-- 
1.5.2.5


^ permalink raw reply related

* [PATCH 6/8] [PATCH] Split up rndis_host.c
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless
In-Reply-To: <11983602942818-git-send-email-bjd@jooz.net>

Split up rndis_host.c into rndis_host.h and rndis_base.c and
change Makefile accordingly. This is done so we can add extra
source files to the rndis_host module later on.

Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/Makefile     |    1 +
 drivers/net/usb/rndis_base.c |  548 ++++++++++++++++++++++++++++++
 drivers/net/usb/rndis_host.c |  763 ------------------------------------------
 drivers/net/usb/rndis_host.h |  256 ++++++++++++++
 4 files changed, 805 insertions(+), 763 deletions(-)
 create mode 100644 drivers/net/usb/rndis_base.c
 delete mode 100644 drivers/net/usb/rndis_host.c
 create mode 100644 drivers/net/usb/rndis_host.h

diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 595a539..611ab62 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_USB_NET_GL620A)	+= gl620a.o
 obj-$(CONFIG_USB_NET_NET1080)	+= net1080.o
 obj-$(CONFIG_USB_NET_PLUSB)	+= plusb.o
 obj-$(CONFIG_USB_NET_RNDIS_HOST)	+= rndis_host.o
+rndis_host-objs			+= rndis_base.o
 obj-$(CONFIG_USB_NET_CDC_SUBSET)	+= cdc_subset.o
 obj-$(CONFIG_USB_NET_ZAURUS)	+= zaurus.o
 obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
diff --git a/drivers/net/usb/rndis_base.c b/drivers/net/usb/rndis_base.c
new file mode 100644
index 0000000..f3b0c00
--- /dev/null
+++ b/drivers/net/usb/rndis_base.c
@@ -0,0 +1,548 @@
+/*
+ * Host Side support for RNDIS Networking Links
+ * Copyright (C) 2005 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// #define	DEBUG			// error path messages, extra info
+// #define	VERBOSE			// more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+
+#include "usbnet.h"
+#include "rndis_host.h"
+
+
+/*
+ * RNDIS is NDIS remoted over USB.  It's a MSFT variant of CDC ACM ... of
+ * course ACM was intended for modems, not Ethernet links!  USB's standard
+ * for Ethernet links is "CDC Ethernet", which is significantly simpler.
+ *
+ * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete.  Issues
+ * include:
+ *    - Power management in particular relies on information that's scattered
+ *	through other documentation, and which is incomplete or incorrect even
+ *	there.
+ *    - There are various undocumented protocol requirements, such as the
+ *	need to send unused garbage in control-OUT messages.
+ *    - In some cases, MS-Windows will emit undocumented requests; this
+ *	matters more to peripheral implementations than host ones.
+ *
+ * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync".
+ *
+ * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in
+ * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and
+ * currently rare) "Ethernet Emulation Model" (EEM).
+ */
+
+/*
+ * RNDIS notifications from device: command completion; "reverse"
+ * keepalives; etc
+ */
+void rndis_status(struct usbnet *dev, struct urb *urb)
+{
+	devdbg(dev, "rndis status urb, len %d stat %d",
+		urb->actual_length, urb->status);
+	// FIXME for keepalives, respond immediately (asynchronously)
+	// if not an RNDIS status, do like cdc_status(dev,urb) does
+}
+EXPORT_SYMBOL_GPL(rndis_status);
+
+/*
+ * RPC done RNDIS-style.  Caller guarantees:
+ * - message is properly byteswapped
+ * - there's no other request pending
+ * - buf can hold up to 1KB response (required by RNDIS spec)
+ * On return, the first few entries are already byteswapped.
+ *
+ * Call context is likely probe(), before interface name is known,
+ * which is why we won't try to use it in the diagnostics.
+ */
+int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
+{
+	struct cdc_state	*info = (void *) &dev->data;
+	int			master_ifnum;
+	int			retval;
+	unsigned		count;
+	__le32			rsp;
+	u32			xid = 0, msg_len, request_id;
+
+	/* REVISIT when this gets called from contexts other than probe() or
+	 * disconnect(): either serialize, or dispatch responses on xid
+	 */
+
+	/* Issue the request; xid is unique, don't bother byteswapping it */
+	if (likely(buf->msg_type != RNDIS_MSG_HALT
+			&& buf->msg_type != RNDIS_MSG_RESET)) {
+		xid = dev->xid++;
+		if (!xid)
+			xid = dev->xid++;
+		buf->request_id = (__force __le32) xid;
+	}
+	master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber;
+	retval = usb_control_msg(dev->udev,
+		usb_sndctrlpipe(dev->udev, 0),
+		USB_CDC_SEND_ENCAPSULATED_COMMAND,
+		USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		0, master_ifnum,
+		buf, le32_to_cpu(buf->msg_len),
+		RNDIS_CONTROL_TIMEOUT_MS);
+	if (unlikely(retval < 0 || xid == 0))
+		return retval;
+
+	// FIXME Seems like some devices discard responses when
+	// we time out and cancel our "get response" requests...
+	// so, this is fragile.  Probably need to poll for status.
+
+	/* ignore status endpoint, just poll the control channel;
+	 * the request probably completed immediately
+	 */
+	rsp = buf->msg_type | RNDIS_MSG_COMPLETION;
+	for (count = 0; count < 10; count++) {
+		memset(buf, 0, CONTROL_BUFFER_SIZE);
+		retval = usb_control_msg(dev->udev,
+			usb_rcvctrlpipe(dev->udev, 0),
+			USB_CDC_GET_ENCAPSULATED_RESPONSE,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			0, master_ifnum,
+			buf, CONTROL_BUFFER_SIZE,
+			RNDIS_CONTROL_TIMEOUT_MS);
+		if (likely(retval >= 8)) {
+			msg_len = le32_to_cpu(buf->msg_len);
+			request_id = (__force u32) buf->request_id;
+			if (likely(buf->msg_type == rsp)) {
+				if (likely(request_id == xid)) {
+					if (unlikely(rsp == RNDIS_MSG_RESET_C))
+						return 0;
+					if (likely(RNDIS_STATUS_SUCCESS
+							== buf->status))
+						return 0;
+					dev_dbg(&info->control->dev,
+						"rndis reply status %08x\n",
+						le32_to_cpu(buf->status));
+					return -EL3RST;
+				}
+				dev_dbg(&info->control->dev,
+					"rndis reply id %d expected %d\n",
+					request_id, xid);
+				/* then likely retry */
+			} else switch (buf->msg_type) {
+			case RNDIS_MSG_INDICATE: {	/* fault */
+				// struct rndis_indicate *msg = (void *)buf;
+				dev_info(&info->control->dev,
+					"rndis fault indication\n");
+				}
+				break;
+			case RNDIS_MSG_KEEPALIVE: {	/* ping */
+				struct rndis_keepalive_c *msg = (void *)buf;
+
+				msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
+				msg->msg_len = ccpu2(sizeof *msg);
+				msg->status = RNDIS_STATUS_SUCCESS;
+				retval = usb_control_msg(dev->udev,
+					usb_sndctrlpipe(dev->udev, 0),
+					USB_CDC_SEND_ENCAPSULATED_COMMAND,
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+					0, master_ifnum,
+					msg, sizeof *msg,
+					RNDIS_CONTROL_TIMEOUT_MS);
+				if (unlikely(retval < 0))
+					dev_dbg(&info->control->dev,
+						"rndis keepalive err %d\n",
+						retval);
+				}
+				break;
+			default:
+				dev_dbg(&info->control->dev,
+					"unexpected rndis msg %08x len %d\n",
+					le32_to_cpu(buf->msg_type), msg_len);
+			}
+		} else {
+			/* device probably issued a protocol stall; ignore */
+			dev_dbg(&info->control->dev,
+				"rndis response error, code %d\n", retval);
+		}
+		msleep(2);
+	}
+	dev_dbg(&info->control->dev, "rndis response timeout\n");
+	return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(rndis_command);
+
+/*
+ * rndis_query:
+ *
+ * Performs a query for @oid along with 0 or more bytes of payload as
+ * specified by @in_len. If @reply_len is not set to -1 then the reply
+ * length is checked against this value, resulting in an error if it
+ * doesn't match.
+ *
+ * NOTE: Adding a payload exactly or greater than the size of the expected
+ * response payload is an evident requirement MSFT added for ActiveSync.
+ *
+ * The only exception is for OIDs that return a variably sized response,
+ * in which case no payload should be added.  This undocumented (and
+ * nonsensical!) issue was found by sniffing protocol requests from the
+ * ActiveSync 4.1 Windows driver.
+ */
+static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
+		void *buf, u32 oid, u32 in_len,
+		void **reply, int *reply_len)
+{
+	int retval;
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_query	*get;
+		struct rndis_query_c	*get_c;
+	} u;
+	u32 off, len;
+
+	u.buf = buf;
+
+	memset(u.get, 0, sizeof *u.get + in_len);
+	u.get->msg_type = RNDIS_MSG_QUERY;
+	u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
+	u.get->oid = oid;
+	u.get->len = cpu_to_le32(in_len);
+	u.get->offset = ccpu2(20);
+
+	retval = rndis_command(dev, u.header);
+	if (unlikely(retval < 0)) {
+		dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
+				oid, retval);
+		return retval;
+	}
+
+	off = le32_to_cpu(u.get_c->offset);
+	len = le32_to_cpu(u.get_c->len);
+	if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
+		goto response_error;
+
+	if (*reply_len != -1 && len != *reply_len)
+		goto response_error;
+
+	*reply = (unsigned char *) &u.get_c->request_id + off;
+	*reply_len = len;
+
+	return retval;
+
+response_error:
+	dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
+			"invalid response - off %d len %d\n",
+		oid, off, len);
+	return -EDOM;
+}
+
+int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int			retval;
+	struct net_device	*net = dev->net;
+	struct cdc_state	*info = (void *) &dev->data;
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_init	*init;
+		struct rndis_init_c	*init_c;
+		struct rndis_query	*get;
+		struct rndis_query_c	*get_c;
+		struct rndis_set	*set;
+		struct rndis_set_c	*set_c;
+		struct rndis_halt	*halt;
+	} u;
+	u32			tmp;
+	int			reply_len;
+	unsigned char		*bp;
+
+	/* we can't rely on i/o from stack working, or stack allocation */
+	u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
+	if (!u.buf)
+		return -ENOMEM;
+	retval = usbnet_generic_cdc_bind(dev, intf);
+	if (retval < 0)
+		goto fail;
+
+	u.init->msg_type = RNDIS_MSG_INIT;
+	u.init->msg_len = ccpu2(sizeof *u.init);
+	u.init->major_version = ccpu2(1);
+	u.init->minor_version = ccpu2(0);
+
+	/* max transfer (in spec) is 0x4000 at full speed, but for
+	 * TX we'll stick to one Ethernet packet plus RNDIS framing.
+	 * For RX we handle drivers that zero-pad to end-of-packet.
+	 * Don't let userspace change these settings.
+	 *
+	 * NOTE: there still seems to be wierdness here, as if we need
+	 * to do some more things to make sure WinCE targets accept this.
+	 * They default to jumbograms of 8KB or 16KB, which is absurd
+	 * for such low data rates and which is also more than Linux
+	 * can usually expect to allocate for SKB data...
+	 */
+	net->hard_header_len += sizeof (struct rndis_data_hdr);
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1);
+	dev->rx_urb_size &= ~(dev->maxpacket - 1);
+	u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size);
+
+	net->change_mtu = NULL;
+	retval = rndis_command(dev, u.header);
+	if (unlikely(retval < 0)) {
+		/* it might not even be an RNDIS device!! */
+		dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
+		goto fail_and_release;
+	}
+	tmp = le32_to_cpu(u.init_c->max_transfer_size);
+	if (tmp < dev->hard_mtu) {
+		if (tmp <= net->hard_header_len) {
+			dev_err(&intf->dev,
+				"dev can't take %u byte packets (max %u)\n",
+				dev->hard_mtu, tmp);
+			retval = -EINVAL;
+			goto halt_fail_and_release;
+		}
+		dev->hard_mtu = tmp;
+		net->mtu = dev->hard_mtu - net->hard_header_len;
+		dev_warn(&intf->dev,
+			 "dev can't take %u byte packets (max %u), "
+			 "adjusting MTU to %u\n",
+			 dev->hard_mtu, tmp, net->mtu);
+	}
+
+	/* REVISIT:  peripheral "alignment" request is ignored ... */
+	dev_dbg(&intf->dev,
+		"hard mtu %u (%u from dev), rx buflen %Zu, align %d\n",
+		dev->hard_mtu, tmp, dev->rx_urb_size,
+		1 << le32_to_cpu(u.init_c->packet_alignment));
+
+	/* Get designated host ethernet address */
+	reply_len = ETH_ALEN;
+	retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
+			48, (void **) &bp, &reply_len);
+	if (unlikely(retval< 0)) {
+		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
+		goto halt_fail_and_release;
+	}
+	memcpy(net->dev_addr, bp, ETH_ALEN);
+
+	/* set a nonzero filter to enable data transfers */
+	memset(u.set, 0, sizeof *u.set);
+	u.set->msg_type = RNDIS_MSG_SET;
+	u.set->msg_len = ccpu2(4 + sizeof *u.set);
+	u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
+	u.set->len = ccpu2(4);
+	u.set->offset = ccpu2((sizeof *u.set) - 8);
+	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
+
+	retval = rndis_command(dev, u.header);
+	if (unlikely(retval < 0)) {
+		dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
+		goto halt_fail_and_release;
+	}
+
+	retval = 0;
+
+	kfree(u.buf);
+	return retval;
+
+halt_fail_and_release:
+	memset(u.halt, 0, sizeof *u.halt);
+	u.halt->msg_type = RNDIS_MSG_HALT;
+	u.halt->msg_len = ccpu2(sizeof *u.halt);
+	(void) rndis_command(dev, (void *)u.halt);
+fail_and_release:
+	usb_set_intfdata(info->data, NULL);
+	usb_driver_release_interface(driver_of(intf), info->data);
+	info->data = NULL;
+fail:
+	kfree(u.buf);
+	return retval;
+}
+EXPORT_SYMBOL_GPL(rndis_bind);
+
+void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct rndis_halt	*halt;
+
+	/* try to clear any rndis state/activity (no i/o from stack!) */
+	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
+	if (halt) {
+		halt->msg_type = RNDIS_MSG_HALT;
+		halt->msg_len = ccpu2(sizeof *halt);
+		(void) rndis_command(dev, (void *)halt);
+		kfree(halt);
+	}
+
+	usbnet_cdc_unbind(dev, intf);
+}
+EXPORT_SYMBOL_GPL(rndis_unbind);
+
+/*
+ * DATA -- host must not write zlps
+ */
+int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	/* peripheral may have batched packets to us... */
+	while (likely(skb->len)) {
+		struct rndis_data_hdr	*hdr = (void *)skb->data;
+		struct sk_buff		*skb2;
+		u32			msg_len, data_offset, data_len;
+
+		msg_len = le32_to_cpu(hdr->msg_len);
+		data_offset = le32_to_cpu(hdr->data_offset);
+		data_len = le32_to_cpu(hdr->data_len);
+
+		/* don't choke if we see oob, per-packet data, etc */
+		if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET
+				|| skb->len < msg_len
+				|| (data_offset + data_len + 8) > msg_len)) {
+			dev->stats.rx_frame_errors++;
+			devdbg(dev, "bad rndis message %d/%d/%d/%d, len %d",
+				le32_to_cpu(hdr->msg_type),
+				msg_len, data_offset, data_len, skb->len);
+			return 0;
+		}
+		skb_pull(skb, 8 + data_offset);
+
+		/* at most one packet left? */
+		if (likely((data_len - skb->len) <= sizeof *hdr)) {
+			skb_trim(skb, data_len);
+			break;
+		}
+
+		/* try to return all the packets in the batch */
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (unlikely(!skb2))
+			break;
+		skb_pull(skb, msg_len - sizeof *hdr);
+		skb_trim(skb2, data_len);
+		usbnet_skb_return(dev, skb2);
+	}
+
+	/* caller will usbnet_skb_return the remaining packet */
+	return 1;
+}
+EXPORT_SYMBOL_GPL(rndis_rx_fixup);
+
+struct sk_buff *
+rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	struct rndis_data_hdr	*hdr;
+	struct sk_buff		*skb2;
+	unsigned		len = skb->len;
+
+	if (likely(!skb_cloned(skb))) {
+		int	room = skb_headroom(skb);
+
+		/* enough head room as-is? */
+		if (unlikely((sizeof *hdr) <= room))
+			goto fill;
+
+		/* enough room, but needs to be readjusted? */
+		room += skb_tailroom(skb);
+		if (likely((sizeof *hdr) <= room)) {
+			skb->data = memmove(skb->head + sizeof *hdr,
+					    skb->data, len);
+			skb_set_tail_pointer(skb, len);
+			goto fill;
+		}
+	}
+
+	/* create a new skb, with the correct size (and tailpad) */
+	skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags);
+	dev_kfree_skb_any(skb);
+	if (unlikely(!skb2))
+		return skb2;
+	skb = skb2;
+
+	/* fill out the RNDIS header.  we won't bother trying to batch
+	 * packets; Linux minimizes wasted bandwidth through tx queues.
+	 */
+fill:
+	hdr = (void *) __skb_push(skb, sizeof *hdr);
+	memset(hdr, 0, sizeof *hdr);
+	hdr->msg_type = RNDIS_MSG_PACKET;
+	hdr->msg_len = cpu_to_le32(skb->len);
+	hdr->data_offset = ccpu2(sizeof(*hdr) - 8);
+	hdr->data_len = cpu_to_le32(len);
+
+	/* FIXME make the last packet always be short ... */
+	return skb;
+}
+EXPORT_SYMBOL_GPL(rndis_tx_fixup);
+
+
+static const struct driver_info	rndis_info = {
+	.description =	"RNDIS device",
+	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.bind =		rndis_bind,
+	.unbind =	rndis_unbind,
+	.status =	rndis_status,
+	.rx_fixup =	rndis_rx_fixup,
+	.tx_fixup =	rndis_tx_fixup,
+};
+
+#undef ccpu2
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct usb_device_id	products [] = {
+{
+	/* RNDIS is MSFT's un-official variant of CDC ACM */
+	USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+	.driver_info = (unsigned long) &rndis_info,
+}, {
+	/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
+	USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
+	.driver_info = (unsigned long) &rndis_info,
+},
+	{ },		// END
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver rndis_driver = {
+	.name =		"rndis_host",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.disconnect =	usbnet_disconnect,
+	.suspend =	usbnet_suspend,
+	.resume =	usbnet_resume,
+};
+
+static int __init rndis_init(void)
+{
+	return usb_register(&rndis_driver);
+}
+module_init(rndis_init);
+
+static void __exit rndis_exit(void)
+{
+	usb_deregister(&rndis_driver);
+}
+module_exit(rndis_exit);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("USB Host side RNDIS driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
deleted file mode 100644
index 3c116f9..0000000
--- a/drivers/net/usb/rndis_host.c
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * Host Side support for RNDIS Networking Links
- * Copyright (C) 2005 by David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-// #define	DEBUG			// error path messages, extra info
-// #define	VERBOSE			// more; success messages
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/workqueue.h>
-#include <linux/mii.h>
-#include <linux/usb.h>
-#include <linux/usb/cdc.h>
-
-#include "usbnet.h"
-
-
-/*
- * RNDIS is NDIS remoted over USB.  It's a MSFT variant of CDC ACM ... of
- * course ACM was intended for modems, not Ethernet links!  USB's standard
- * for Ethernet links is "CDC Ethernet", which is significantly simpler.
- *
- * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete.  Issues
- * include:
- *    - Power management in particular relies on information that's scattered
- *	through other documentation, and which is incomplete or incorrect even
- *	there.
- *    - There are various undocumented protocol requirements, such as the
- *	need to send unused garbage in control-OUT messages.
- *    - In some cases, MS-Windows will emit undocumented requests; this
- *	matters more to peripheral implementations than host ones.
- *
- * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync".
- *
- * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in
- * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and
- * currently rare) "Ethernet Emulation Model" (EEM).
- */
-
-/*
- * CONTROL uses CDC "encapsulated commands" with funky notifications.
- *  - control-out:  SEND_ENCAPSULATED
- *  - interrupt-in:  RESPONSE_AVAILABLE
- *  - control-in:  GET_ENCAPSULATED
- *
- * We'll try to ignore the RESPONSE_AVAILABLE notifications.
- *
- * REVISIT some RNDIS implementations seem to have curious issues still
- * to be resolved.
- */
-struct rndis_msg_hdr {
-	__le32	msg_type;			/* RNDIS_MSG_* */
-	__le32	msg_len;
-	// followed by data that varies between messages
-	__le32	request_id;
-	__le32	status;
-	// ... and more
-} __attribute__ ((packed));
-
-/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */
-#define	CONTROL_BUFFER_SIZE		1025
-
-/* RNDIS defines an (absurdly huge) 10 second control timeout,
- * but ActiveSync seems to use a more usual 5 second timeout
- * (which matches the USB 2.0 spec).
- */
-#define	RNDIS_CONTROL_TIMEOUT_MS	(5 * 1000)
-
-
-#define ccpu2 __constant_cpu_to_le32
-
-#define RNDIS_MSG_COMPLETION	ccpu2(0x80000000)
-
-/* codes for "msg_type" field of rndis messages;
- * only the data channel uses packet messages (maybe batched);
- * everything else goes on the control channel.
- */
-#define RNDIS_MSG_PACKET	ccpu2(0x00000001)	/* 1-N packets */
-#define RNDIS_MSG_INIT		ccpu2(0x00000002)
-#define RNDIS_MSG_INIT_C	(RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_HALT		ccpu2(0x00000003)
-#define RNDIS_MSG_QUERY		ccpu2(0x00000004)
-#define RNDIS_MSG_QUERY_C	(RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_SET		ccpu2(0x00000005)
-#define RNDIS_MSG_SET_C		(RNDIS_MSG_SET|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_RESET		ccpu2(0x00000006)
-#define RNDIS_MSG_RESET_C	(RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_INDICATE	ccpu2(0x00000007)
-#define RNDIS_MSG_KEEPALIVE	ccpu2(0x00000008)
-#define RNDIS_MSG_KEEPALIVE_C	(RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION)
-
-/* codes for "status" field of completion messages */
-#define	RNDIS_STATUS_SUCCESS		ccpu2(0x00000000)
-#define	RNDIS_STATUS_FAILURE		ccpu2(0xc0000001)
-#define	RNDIS_STATUS_INVALID_DATA	ccpu2(0xc0010015)
-#define	RNDIS_STATUS_NOT_SUPPORTED	ccpu2(0xc00000bb)
-#define	RNDIS_STATUS_MEDIA_CONNECT	ccpu2(0x4001000b)
-#define	RNDIS_STATUS_MEDIA_DISCONNECT	ccpu2(0x4001000c)
-
-
-struct rndis_data_hdr {
-	__le32	msg_type;		/* RNDIS_MSG_PACKET */
-	__le32	msg_len;		// rndis_data_hdr + data_len + pad
-	__le32	data_offset;		// 36 -- right after header
-	__le32	data_len;		// ... real packet size
-
-	__le32	oob_data_offset;	// zero
-	__le32	oob_data_len;		// zero
-	__le32	num_oob;		// zero
-	__le32	packet_data_offset;	// zero
-
-	__le32	packet_data_len;	// zero
-	__le32	vc_handle;		// zero
-	__le32	reserved;		// zero
-} __attribute__ ((packed));
-
-struct rndis_init {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_INIT */
-	__le32	msg_len;			// 24
-	__le32	request_id;
-	__le32	major_version;			// of rndis (1.0)
-	__le32	minor_version;
-	__le32	max_transfer_size;
-} __attribute__ ((packed));
-
-struct rndis_init_c {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_INIT_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-	__le32	major_version;			// of rndis (1.0)
-	__le32	minor_version;
-	__le32	device_flags;
-	__le32	medium;				// zero == 802.3
-	__le32	max_packets_per_message;
-	__le32	max_transfer_size;
-	__le32	packet_alignment;		// max 7; (1<<n) bytes
-	__le32	af_list_offset;			// zero
-	__le32	af_list_size;			// zero
-} __attribute__ ((packed));
-
-struct rndis_halt {		/* OUT (no reply) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_HALT */
-	__le32	msg_len;
-	__le32	request_id;
-} __attribute__ ((packed));
-
-struct rndis_query {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_QUERY */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	oid;
-	__le32	len;
-	__le32	offset;
-/*?*/	__le32	handle;				// zero
-} __attribute__ ((packed));
-
-struct rndis_query_c {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_QUERY_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-	__le32	len;
-	__le32	offset;
-} __attribute__ ((packed));
-
-struct rndis_set {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_SET */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	oid;
-	__le32	len;
-	__le32	offset;
-/*?*/	__le32	handle;				// zero
-} __attribute__ ((packed));
-
-struct rndis_set_c {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_SET_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-} __attribute__ ((packed));
-
-struct rndis_reset {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_RESET */
-	__le32	msg_len;
-	__le32	reserved;
-} __attribute__ ((packed));
-
-struct rndis_reset_c {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_RESET_C */
-	__le32	msg_len;
-	__le32	status;
-	__le32	addressing_lost;
-} __attribute__ ((packed));
-
-struct rndis_indicate {		/* IN (unrequested) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_INDICATE */
-	__le32	msg_len;
-	__le32	status;
-	__le32	length;
-	__le32	offset;
-/**/	__le32	diag_status;
-	__le32	error_offset;
-/**/	__le32	message;
-} __attribute__ ((packed));
-
-struct rndis_keepalive {	/* OUT (optionally IN) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE */
-	__le32	msg_len;
-	__le32	request_id;
-} __attribute__ ((packed));
-
-struct rndis_keepalive_c {	/* IN (optionally OUT) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-} __attribute__ ((packed));
-
-/* NOTE:  about 30 OIDs are "mandatory" for peripherals to support ... and
- * there are gobs more that may optionally be supported.  We'll avoid as much
- * of that mess as possible.
- */
-#define OID_802_3_PERMANENT_ADDRESS	ccpu2(0x01010101)
-#define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
-#define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
-
-/* packet filter bits used by NDIS_OID_PACKET_FILTER */
-#define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
-#define RNDIS_PACKET_TYPE_MULTICAST		ccpu2(0x00000002)
-#define RNDIS_PACKET_TYPE_ALL_MULTICAST		ccpu2(0x00000004)
-#define RNDIS_PACKET_TYPE_BROADCAST		ccpu2(0x00000008)
-#define RNDIS_PACKET_TYPE_SOURCE_ROUTING	ccpu2(0x00000010)
-#define RNDIS_PACKET_TYPE_PROMISCUOUS		ccpu2(0x00000020)
-#define RNDIS_PACKET_TYPE_SMT			ccpu2(0x00000040)
-#define RNDIS_PACKET_TYPE_ALL_LOCAL		ccpu2(0x00000080)
-#define RNDIS_PACKET_TYPE_GROUP			ccpu2(0x00001000)
-#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL	ccpu2(0x00002000)
-#define RNDIS_PACKET_TYPE_FUNCTIONAL		ccpu2(0x00004000)
-#define RNDIS_PACKET_TYPE_MAC_FRAME		ccpu2(0x00008000)
-
-/* default filter used with RNDIS devices */
-#define RNDIS_DEFAULT_FILTER ( \
-	RNDIS_PACKET_TYPE_DIRECTED | \
-	RNDIS_PACKET_TYPE_BROADCAST | \
-	RNDIS_PACKET_TYPE_ALL_MULTICAST | \
-	RNDIS_PACKET_TYPE_PROMISCUOUS )
-
-/*
- * RNDIS notifications from device: command completion; "reverse"
- * keepalives; etc
- */
-static void rndis_status(struct usbnet *dev, struct urb *urb)
-{
-	devdbg(dev, "rndis status urb, len %d stat %d",
-		urb->actual_length, urb->status);
-	// FIXME for keepalives, respond immediately (asynchronously)
-	// if not an RNDIS status, do like cdc_status(dev,urb) does
-}
-
-/*
- * RPC done RNDIS-style.  Caller guarantees:
- * - message is properly byteswapped
- * - there's no other request pending
- * - buf can hold up to 1KB response (required by RNDIS spec)
- * On return, the first few entries are already byteswapped.
- *
- * Call context is likely probe(), before interface name is known,
- * which is why we won't try to use it in the diagnostics.
- */
-static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
-{
-	struct cdc_state	*info = (void *) &dev->data;
-	int			master_ifnum;
-	int			retval;
-	unsigned		count;
-	__le32			rsp;
-	u32			xid = 0, msg_len, request_id;
-
-	/* REVISIT when this gets called from contexts other than probe() or
-	 * disconnect(): either serialize, or dispatch responses on xid
-	 */
-
-	/* Issue the request; xid is unique, don't bother byteswapping it */
-	if (likely(buf->msg_type != RNDIS_MSG_HALT
-			&& buf->msg_type != RNDIS_MSG_RESET)) {
-		xid = dev->xid++;
-		if (!xid)
-			xid = dev->xid++;
-		buf->request_id = (__force __le32) xid;
-	}
-	master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber;
-	retval = usb_control_msg(dev->udev,
-		usb_sndctrlpipe(dev->udev, 0),
-		USB_CDC_SEND_ENCAPSULATED_COMMAND,
-		USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-		0, master_ifnum,
-		buf, le32_to_cpu(buf->msg_len),
-		RNDIS_CONTROL_TIMEOUT_MS);
-	if (unlikely(retval < 0 || xid == 0))
-		return retval;
-
-	// FIXME Seems like some devices discard responses when
-	// we time out and cancel our "get response" requests...
-	// so, this is fragile.  Probably need to poll for status.
-
-	/* ignore status endpoint, just poll the control channel;
-	 * the request probably completed immediately
-	 */
-	rsp = buf->msg_type | RNDIS_MSG_COMPLETION;
-	for (count = 0; count < 10; count++) {
-		memset(buf, 0, CONTROL_BUFFER_SIZE);
-		retval = usb_control_msg(dev->udev,
-			usb_rcvctrlpipe(dev->udev, 0),
-			USB_CDC_GET_ENCAPSULATED_RESPONSE,
-			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-			0, master_ifnum,
-			buf, CONTROL_BUFFER_SIZE,
-			RNDIS_CONTROL_TIMEOUT_MS);
-		if (likely(retval >= 8)) {
-			msg_len = le32_to_cpu(buf->msg_len);
-			request_id = (__force u32) buf->request_id;
-			if (likely(buf->msg_type == rsp)) {
-				if (likely(request_id == xid)) {
-					if (unlikely(rsp == RNDIS_MSG_RESET_C))
-						return 0;
-					if (likely(RNDIS_STATUS_SUCCESS
-							== buf->status))
-						return 0;
-					dev_dbg(&info->control->dev,
-						"rndis reply status %08x\n",
-						le32_to_cpu(buf->status));
-					return -EL3RST;
-				}
-				dev_dbg(&info->control->dev,
-					"rndis reply id %d expected %d\n",
-					request_id, xid);
-				/* then likely retry */
-			} else switch (buf->msg_type) {
-			case RNDIS_MSG_INDICATE: {	/* fault */
-				// struct rndis_indicate *msg = (void *)buf;
-				dev_info(&info->control->dev,
-					"rndis fault indication\n");
-				}
-				break;
-			case RNDIS_MSG_KEEPALIVE: {	/* ping */
-				struct rndis_keepalive_c *msg = (void *)buf;
-
-				msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
-				msg->msg_len = ccpu2(sizeof *msg);
-				msg->status = RNDIS_STATUS_SUCCESS;
-				retval = usb_control_msg(dev->udev,
-					usb_sndctrlpipe(dev->udev, 0),
-					USB_CDC_SEND_ENCAPSULATED_COMMAND,
-					USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-					0, master_ifnum,
-					msg, sizeof *msg,
-					RNDIS_CONTROL_TIMEOUT_MS);
-				if (unlikely(retval < 0))
-					dev_dbg(&info->control->dev,
-						"rndis keepalive err %d\n",
-						retval);
-				}
-				break;
-			default:
-				dev_dbg(&info->control->dev,
-					"unexpected rndis msg %08x len %d\n",
-					le32_to_cpu(buf->msg_type), msg_len);
-			}
-		} else {
-			/* device probably issued a protocol stall; ignore */
-			dev_dbg(&info->control->dev,
-				"rndis response error, code %d\n", retval);
-		}
-		msleep(2);
-	}
-	dev_dbg(&info->control->dev, "rndis response timeout\n");
-	return -ETIMEDOUT;
-}
-
-/*
- * rndis_query:
- *
- * Performs a query for @oid along with 0 or more bytes of payload as
- * specified by @in_len. If @reply_len is not set to -1 then the reply
- * length is checked against this value, resulting in an error if it
- * doesn't match.
- *
- * NOTE: Adding a payload exactly or greater than the size of the expected
- * response payload is an evident requirement MSFT added for ActiveSync.
- *
- * The only exception is for OIDs that return a variably sized response,
- * in which case no payload should be added.  This undocumented (and
- * nonsensical!) issue was found by sniffing protocol requests from the
- * ActiveSync 4.1 Windows driver.
- */
-static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
-		void *buf, u32 oid, u32 in_len,
-		void **reply, int *reply_len)
-{
-	int retval;
-	union {
-		void			*buf;
-		struct rndis_msg_hdr	*header;
-		struct rndis_query	*get;
-		struct rndis_query_c	*get_c;
-	} u;
-	u32 off, len;
-
-	u.buf = buf;
-
-	memset(u.get, 0, sizeof *u.get + in_len);
-	u.get->msg_type = RNDIS_MSG_QUERY;
-	u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
-	u.get->oid = oid;
-	u.get->len = cpu_to_le32(in_len);
-	u.get->offset = ccpu2(20);
-
-	retval = rndis_command(dev, u.header);
-	if (unlikely(retval < 0)) {
-		dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
-				oid, retval);
-		return retval;
-	}
-
-	off = le32_to_cpu(u.get_c->offset);
-	len = le32_to_cpu(u.get_c->len);
-	if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
-		goto response_error;
-
-	if (*reply_len != -1 && len != *reply_len)
-		goto response_error;
-
-	*reply = (unsigned char *) &u.get_c->request_id + off;
-	*reply_len = len;
-
-	return retval;
-
-response_error:
-	dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
-			"invalid response - off %d len %d\n",
-		oid, off, len);
-	return -EDOM;
-}
-
-static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
-{
-	int			retval;
-	struct net_device	*net = dev->net;
-	struct cdc_state	*info = (void *) &dev->data;
-	union {
-		void			*buf;
-		struct rndis_msg_hdr	*header;
-		struct rndis_init	*init;
-		struct rndis_init_c	*init_c;
-		struct rndis_query	*get;
-		struct rndis_query_c	*get_c;
-		struct rndis_set	*set;
-		struct rndis_set_c	*set_c;
-		struct rndis_halt	*halt;
-	} u;
-	u32			tmp;
-	int			reply_len;
-	unsigned char		*bp;
-
-	/* we can't rely on i/o from stack working, or stack allocation */
-	u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
-	if (!u.buf)
-		return -ENOMEM;
-	retval = usbnet_generic_cdc_bind(dev, intf);
-	if (retval < 0)
-		goto fail;
-
-	u.init->msg_type = RNDIS_MSG_INIT;
-	u.init->msg_len = ccpu2(sizeof *u.init);
-	u.init->major_version = ccpu2(1);
-	u.init->minor_version = ccpu2(0);
-
-	/* max transfer (in spec) is 0x4000 at full speed, but for
-	 * TX we'll stick to one Ethernet packet plus RNDIS framing.
-	 * For RX we handle drivers that zero-pad to end-of-packet.
-	 * Don't let userspace change these settings.
-	 *
-	 * NOTE: there still seems to be wierdness here, as if we need
-	 * to do some more things to make sure WinCE targets accept this.
-	 * They default to jumbograms of 8KB or 16KB, which is absurd
-	 * for such low data rates and which is also more than Linux
-	 * can usually expect to allocate for SKB data...
-	 */
-	net->hard_header_len += sizeof (struct rndis_data_hdr);
-	dev->hard_mtu = net->mtu + net->hard_header_len;
-
-	dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1);
-	dev->rx_urb_size &= ~(dev->maxpacket - 1);
-	u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size);
-
-	net->change_mtu = NULL;
-	retval = rndis_command(dev, u.header);
-	if (unlikely(retval < 0)) {
-		/* it might not even be an RNDIS device!! */
-		dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
-		goto fail_and_release;
-	}
-	tmp = le32_to_cpu(u.init_c->max_transfer_size);
-	if (tmp < dev->hard_mtu) {
-		if (tmp <= net->hard_header_len) {
-			dev_err(&intf->dev,
-				"dev can't take %u byte packets (max %u)\n",
-				dev->hard_mtu, tmp);
-			retval = -EINVAL;
-			goto halt_fail_and_release;
-		}
-		dev->hard_mtu = tmp;
-		net->mtu = dev->hard_mtu - net->hard_header_len;
-		dev_warn(&intf->dev,
-			 "dev can't take %u byte packets (max %u), "
-			 "adjusting MTU to %u\n",
-			 dev->hard_mtu, tmp, net->mtu);
-	}
-
-	/* REVISIT:  peripheral "alignment" request is ignored ... */
-	dev_dbg(&intf->dev,
-		"hard mtu %u (%u from dev), rx buflen %Zu, align %d\n",
-		dev->hard_mtu, tmp, dev->rx_urb_size,
-		1 << le32_to_cpu(u.init_c->packet_alignment));
-
-	/* Get designated host ethernet address */
-	reply_len = ETH_ALEN;
-	retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
-			48, (void **) &bp, &reply_len);
-	if (unlikely(retval< 0)) {
-		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
-		goto halt_fail_and_release;
-	}
-	memcpy(net->dev_addr, bp, ETH_ALEN);
-
-	/* set a nonzero filter to enable data transfers */
-	memset(u.set, 0, sizeof *u.set);
-	u.set->msg_type = RNDIS_MSG_SET;
-	u.set->msg_len = ccpu2(4 + sizeof *u.set);
-	u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
-	u.set->len = ccpu2(4);
-	u.set->offset = ccpu2((sizeof *u.set) - 8);
-	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
-
-	retval = rndis_command(dev, u.header);
-	if (unlikely(retval < 0)) {
-		dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
-		goto halt_fail_and_release;
-	}
-
-	retval = 0;
-
-	kfree(u.buf);
-	return retval;
-
-halt_fail_and_release:
-	memset(u.halt, 0, sizeof *u.halt);
-	u.halt->msg_type = RNDIS_MSG_HALT;
-	u.halt->msg_len = ccpu2(sizeof *u.halt);
-	(void) rndis_command(dev, (void *)u.halt);
-fail_and_release:
-	usb_set_intfdata(info->data, NULL);
-	usb_driver_release_interface(driver_of(intf), info->data);
-	info->data = NULL;
-fail:
-	kfree(u.buf);
-	return retval;
-}
-
-static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
-{
-	struct rndis_halt	*halt;
-
-	/* try to clear any rndis state/activity (no i/o from stack!) */
-	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
-	if (halt) {
-		halt->msg_type = RNDIS_MSG_HALT;
-		halt->msg_len = ccpu2(sizeof *halt);
-		(void) rndis_command(dev, (void *)halt);
-		kfree(halt);
-	}
-
-	usbnet_cdc_unbind(dev, intf);
-}
-
-/*
- * DATA -- host must not write zlps
- */
-static int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
-{
-	/* peripheral may have batched packets to us... */
-	while (likely(skb->len)) {
-		struct rndis_data_hdr	*hdr = (void *)skb->data;
-		struct sk_buff		*skb2;
-		u32			msg_len, data_offset, data_len;
-
-		msg_len = le32_to_cpu(hdr->msg_len);
-		data_offset = le32_to_cpu(hdr->data_offset);
-		data_len = le32_to_cpu(hdr->data_len);
-
-		/* don't choke if we see oob, per-packet data, etc */
-		if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET
-				|| skb->len < msg_len
-				|| (data_offset + data_len + 8) > msg_len)) {
-			dev->stats.rx_frame_errors++;
-			devdbg(dev, "bad rndis message %d/%d/%d/%d, len %d",
-				le32_to_cpu(hdr->msg_type),
-				msg_len, data_offset, data_len, skb->len);
-			return 0;
-		}
-		skb_pull(skb, 8 + data_offset);
-
-		/* at most one packet left? */
-		if (likely((data_len - skb->len) <= sizeof *hdr)) {
-			skb_trim(skb, data_len);
-			break;
-		}
-
-		/* try to return all the packets in the batch */
-		skb2 = skb_clone(skb, GFP_ATOMIC);
-		if (unlikely(!skb2))
-			break;
-		skb_pull(skb, msg_len - sizeof *hdr);
-		skb_trim(skb2, data_len);
-		usbnet_skb_return(dev, skb2);
-	}
-
-	/* caller will usbnet_skb_return the remaining packet */
-	return 1;
-}
-
-static struct sk_buff *
-rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
-{
-	struct rndis_data_hdr	*hdr;
-	struct sk_buff		*skb2;
-	unsigned		len = skb->len;
-
-	if (likely(!skb_cloned(skb))) {
-		int	room = skb_headroom(skb);
-
-		/* enough head room as-is? */
-		if (unlikely((sizeof *hdr) <= room))
-			goto fill;
-
-		/* enough room, but needs to be readjusted? */
-		room += skb_tailroom(skb);
-		if (likely((sizeof *hdr) <= room)) {
-			skb->data = memmove(skb->head + sizeof *hdr,
-					    skb->data, len);
-			skb_set_tail_pointer(skb, len);
-			goto fill;
-		}
-	}
-
-	/* create a new skb, with the correct size (and tailpad) */
-	skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags);
-	dev_kfree_skb_any(skb);
-	if (unlikely(!skb2))
-		return skb2;
-	skb = skb2;
-
-	/* fill out the RNDIS header.  we won't bother trying to batch
-	 * packets; Linux minimizes wasted bandwidth through tx queues.
-	 */
-fill:
-	hdr = (void *) __skb_push(skb, sizeof *hdr);
-	memset(hdr, 0, sizeof *hdr);
-	hdr->msg_type = RNDIS_MSG_PACKET;
-	hdr->msg_len = cpu_to_le32(skb->len);
-	hdr->data_offset = ccpu2(sizeof(*hdr) - 8);
-	hdr->data_len = cpu_to_le32(len);
-
-	/* FIXME make the last packet always be short ... */
-	return skb;
-}
-
-
-static const struct driver_info	rndis_info = {
-	.description =	"RNDIS device",
-	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
-	.bind =		rndis_bind,
-	.unbind =	rndis_unbind,
-	.status =	rndis_status,
-	.rx_fixup =	rndis_rx_fixup,
-	.tx_fixup =	rndis_tx_fixup,
-};
-
-#undef ccpu2
-
-
-/*-------------------------------------------------------------------------*/
-
-static const struct usb_device_id	products [] = {
-{
-	/* RNDIS is MSFT's un-official variant of CDC ACM */
-	USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
-	.driver_info = (unsigned long) &rndis_info,
-}, {
-	/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
-	USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
-	.driver_info = (unsigned long) &rndis_info,
-},
-	{ },		// END
-};
-MODULE_DEVICE_TABLE(usb, products);
-
-static struct usb_driver rndis_driver = {
-	.name =		"rndis_host",
-	.id_table =	products,
-	.probe =	usbnet_probe,
-	.disconnect =	usbnet_disconnect,
-	.suspend =	usbnet_suspend,
-	.resume =	usbnet_resume,
-};
-
-static int __init rndis_init(void)
-{
-	return usb_register(&rndis_driver);
-}
-module_init(rndis_init);
-
-static void __exit rndis_exit(void)
-{
-	usb_deregister(&rndis_driver);
-}
-module_exit(rndis_exit);
-
-MODULE_AUTHOR("David Brownell");
-MODULE_DESCRIPTION("USB Host side RNDIS driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/rndis_host.h b/drivers/net/usb/rndis_host.h
new file mode 100644
index 0000000..56de532
--- /dev/null
+++ b/drivers/net/usb/rndis_host.h
@@ -0,0 +1,256 @@
+/*
+ * Host Side support for RNDIS Networking Links
+ * Copyright (C) 2005 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef	__RNDIS_HOST_H
+#define	__RNDIS_HOST_H
+
+
+/*
+ * CONTROL uses CDC "encapsulated commands" with funky notifications.
+ *  - control-out:  SEND_ENCAPSULATED
+ *  - interrupt-in:  RESPONSE_AVAILABLE
+ *  - control-in:  GET_ENCAPSULATED
+ *
+ * We'll try to ignore the RESPONSE_AVAILABLE notifications.
+ *
+ * REVISIT some RNDIS implementations seem to have curious issues still
+ * to be resolved.
+ */
+struct rndis_msg_hdr {
+	__le32	msg_type;			/* RNDIS_MSG_* */
+	__le32	msg_len;
+	// followed by data that varies between messages
+	__le32	request_id;
+	__le32	status;
+	// ... and more
+} __attribute__ ((packed));
+
+/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */
+#define	CONTROL_BUFFER_SIZE		1025
+
+/* RNDIS defines an (absurdly huge) 10 second control timeout,
+ * but ActiveSync seems to use a more usual 5 second timeout
+ * (which matches the USB 2.0 spec).
+ */
+#define	RNDIS_CONTROL_TIMEOUT_MS	(5 * 1000)
+
+
+#define ccpu2 __constant_cpu_to_le32
+
+#define RNDIS_MSG_COMPLETION	ccpu2(0x80000000)
+
+/* codes for "msg_type" field of rndis messages;
+ * only the data channel uses packet messages (maybe batched);
+ * everything else goes on the control channel.
+ */
+#define RNDIS_MSG_PACKET	ccpu2(0x00000001)	/* 1-N packets */
+#define RNDIS_MSG_INIT		ccpu2(0x00000002)
+#define RNDIS_MSG_INIT_C	(RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_HALT		ccpu2(0x00000003)
+#define RNDIS_MSG_QUERY		ccpu2(0x00000004)
+#define RNDIS_MSG_QUERY_C	(RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_SET		ccpu2(0x00000005)
+#define RNDIS_MSG_SET_C		(RNDIS_MSG_SET|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_RESET		ccpu2(0x00000006)
+#define RNDIS_MSG_RESET_C	(RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_INDICATE	ccpu2(0x00000007)
+#define RNDIS_MSG_KEEPALIVE	ccpu2(0x00000008)
+#define RNDIS_MSG_KEEPALIVE_C	(RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION)
+
+/* codes for "status" field of completion messages */
+#define	RNDIS_STATUS_SUCCESS		ccpu2(0x00000000)
+#define	RNDIS_STATUS_FAILURE		ccpu2(0xc0000001)
+#define	RNDIS_STATUS_INVALID_DATA	ccpu2(0xc0010015)
+#define	RNDIS_STATUS_NOT_SUPPORTED	ccpu2(0xc00000bb)
+#define	RNDIS_STATUS_MEDIA_CONNECT	ccpu2(0x4001000b)
+#define	RNDIS_STATUS_MEDIA_DISCONNECT	ccpu2(0x4001000c)
+
+
+struct rndis_data_hdr {
+	__le32	msg_type;		/* RNDIS_MSG_PACKET */
+	__le32	msg_len;		// rndis_data_hdr + data_len + pad
+	__le32	data_offset;		// 36 -- right after header
+	__le32	data_len;		// ... real packet size
+
+	__le32	oob_data_offset;	// zero
+	__le32	oob_data_len;		// zero
+	__le32	num_oob;		// zero
+	__le32	packet_data_offset;	// zero
+
+	__le32	packet_data_len;	// zero
+	__le32	vc_handle;		// zero
+	__le32	reserved;		// zero
+} __attribute__ ((packed));
+
+struct rndis_init {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_INIT */
+	__le32	msg_len;			// 24
+	__le32	request_id;
+	__le32	major_version;			// of rndis (1.0)
+	__le32	minor_version;
+	__le32	max_transfer_size;
+} __attribute__ ((packed));
+
+struct rndis_init_c {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_INIT_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+	__le32	major_version;			// of rndis (1.0)
+	__le32	minor_version;
+	__le32	device_flags;
+	__le32	medium;				// zero == 802.3
+	__le32	max_packets_per_message;
+	__le32	max_transfer_size;
+	__le32	packet_alignment;		// max 7; (1<<n) bytes
+	__le32	af_list_offset;			// zero
+	__le32	af_list_size;			// zero
+} __attribute__ ((packed));
+
+struct rndis_halt {		/* OUT (no reply) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_HALT */
+	__le32	msg_len;
+	__le32	request_id;
+} __attribute__ ((packed));
+
+struct rndis_query {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_QUERY */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	oid;
+	__le32	len;
+	__le32	offset;
+/*?*/	__le32	handle;				// zero
+} __attribute__ ((packed));
+
+struct rndis_query_c {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_QUERY_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+	__le32	len;
+	__le32	offset;
+} __attribute__ ((packed));
+
+struct rndis_set {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_SET */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	oid;
+	__le32	len;
+	__le32	offset;
+/*?*/	__le32	handle;				// zero
+} __attribute__ ((packed));
+
+struct rndis_set_c {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_SET_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+} __attribute__ ((packed));
+
+struct rndis_reset {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_RESET */
+	__le32	msg_len;
+	__le32	reserved;
+} __attribute__ ((packed));
+
+struct rndis_reset_c {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_RESET_C */
+	__le32	msg_len;
+	__le32	status;
+	__le32	addressing_lost;
+} __attribute__ ((packed));
+
+struct rndis_indicate {		/* IN (unrequested) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_INDICATE */
+	__le32	msg_len;
+	__le32	status;
+	__le32	length;
+	__le32	offset;
+/**/	__le32	diag_status;
+	__le32	error_offset;
+/**/	__le32	message;
+} __attribute__ ((packed));
+
+struct rndis_keepalive {	/* OUT (optionally IN) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE */
+	__le32	msg_len;
+	__le32	request_id;
+} __attribute__ ((packed));
+
+struct rndis_keepalive_c {	/* IN (optionally OUT) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+} __attribute__ ((packed));
+
+/* NOTE:  about 30 OIDs are "mandatory" for peripherals to support ... and
+ * there are gobs more that may optionally be supported.  We'll avoid as much
+ * of that mess as possible.
+ */
+#define OID_802_3_PERMANENT_ADDRESS	ccpu2(0x01010101)
+#define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
+#define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
+
+/* packet filter bits used by NDIS_OID_PACKET_FILTER */
+#define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
+#define RNDIS_PACKET_TYPE_MULTICAST		ccpu2(0x00000002)
+#define RNDIS_PACKET_TYPE_ALL_MULTICAST		ccpu2(0x00000004)
+#define RNDIS_PACKET_TYPE_BROADCAST		ccpu2(0x00000008)
+#define RNDIS_PACKET_TYPE_SOURCE_ROUTING	ccpu2(0x00000010)
+#define RNDIS_PACKET_TYPE_PROMISCUOUS		ccpu2(0x00000020)
+#define RNDIS_PACKET_TYPE_SMT			ccpu2(0x00000040)
+#define RNDIS_PACKET_TYPE_ALL_LOCAL		ccpu2(0x00000080)
+#define RNDIS_PACKET_TYPE_GROUP			ccpu2(0x00001000)
+#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL	ccpu2(0x00002000)
+#define RNDIS_PACKET_TYPE_FUNCTIONAL		ccpu2(0x00004000)
+#define RNDIS_PACKET_TYPE_MAC_FRAME		ccpu2(0x00008000)
+
+/* default filter used with RNDIS devices */
+#define RNDIS_DEFAULT_FILTER ( \
+	RNDIS_PACKET_TYPE_DIRECTED | \
+	RNDIS_PACKET_TYPE_BROADCAST | \
+	RNDIS_PACKET_TYPE_ALL_MULTICAST | \
+	RNDIS_PACKET_TYPE_PROMISCUOUS )
+
+extern void rndis_status(struct usbnet *dev, struct urb *urb);
+extern int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf);
+extern int rndis_bind(struct usbnet *dev, struct usb_interface *intf);
+extern void rndis_unbind(struct usbnet *dev, struct usb_interface *intf);
+extern int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb);
+extern struct sk_buff *
+rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags);
+
+#endif	/* __RNDIS_HOST_H */
+
-- 
1.5.2.5


^ permalink raw reply related

* [PATCH 0/8] RFC: Wireless Extensions for rndis_host
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless

Hello all,

I have here a patchset that needs some review.
This patchset adds wireless extensions for rndis_host to
enable support for RNDIS based USB wireless LAN adapters
(e.g. Linksys WUSB54GS, Belkin F05D7051).

In this patchset:
 1.  Fix sparse warning: returning void valued expression
 2.  Hardwire CDC descriptors when missing
 3.  Use 1KB buffer in rndis_unbind
 4.  Halt device if rndis_bind fails
 5.  Fix rndis packet filter flags
 6.  Split up rndis_host.c
 7.  Add Wireless Extensions for rndis_host
 8.  Use wlan device name for RNDIS wireless devices

The first five patches are more generic fixes that I think
can be applied as they are. Of these, patch 2 is required
to get these wireless devices working. 
Patches 6-8 introduce some larger changes that may need a
closer look.

All these patches should be applied in order.
The entire series should apply cleanly to current linux
kernel and net-2.6.25 trees.

regards,
Bjorge Dijkstra

^ permalink raw reply

* Re: [PATCH 1/8] Fix sparse warning: returning void-valued expression
From: David Brownell @ 2007-12-23  0:42 UTC (permalink / raw)
  To: bjd-a1rhEgazXTw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <11983602952341-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>

> From bjd-a1rhEgazXTw@public.gmane.org  Sat Dec 22 13:52:53 2007
> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Subject: [PATCH 1/8] Fix sparse warning: returning void-valued expression
> Date: Sat, 22 Dec 2007 22:51:27 +0100
>
> rndis_unbind and usbnet_cdc_unbind don't return anything.
>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>

Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>


> ---
>  drivers/net/usb/rndis_host.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 1ebe325..96ef6a9 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -585,7 +585,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
>  		kfree(halt);
>  	}
>  
> -	return usbnet_cdc_unbind(dev, intf);
> +	usbnet_cdc_unbind(dev, intf);
>  }
>  
>  /*
> -- 
> 1.5.2.5
>

^ permalink raw reply

* Re: [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing
From: David Brownell @ 2007-12-23  0:49 UTC (permalink / raw)
  To: bjd; +Cc: netdev, linux-wireless
In-Reply-To: <11983602953487-git-send-email-bjd@jooz.net>

> From bjd@jooz.net  Sat Dec 22 13:53:00 2007
> X-SourceIP: 213.93.131.145
> From: Bjorge Dijkstra <bjd@jooz.net>
> To: dbrownell@users.sourceforge.net
> Cc: netdev@vger.kernel.org, linux-wireless@vger.kernel.org
> Subject: [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing
> Date: Sat, 22 Dec 2007 22:51:28 +0100
>
> Just as ActiveSync devices, some regular RNDIS devices also lack
> the CDC descriptors (e.g. devices based on BCM4320 WLAN chip).
> This patch hardwires the CDC descriptors for all RNDIS style devices
> when they are missing.
>
> Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>

Acked-by: David Brownell <dbrownell@users.sourceforge.net>

Note what this tells us about how little Microsoft cares about
the interoperability specs they claim to conform to.  Having
those descriptors is *NOT* optional in their (incomplete and
in adequate) RNDIS specification.  (I've not seen ActiveSync
specs, so that support is pure guesswork.)


> ---
>  drivers/net/usb/cdc_ether.c |   10 +++++-----
>  1 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
> index a42acc3..97c17bb 100644
> --- a/drivers/net/usb/cdc_ether.c
> +++ b/drivers/net/usb/cdc_ether.c
> @@ -228,15 +228,16 @@ next_desc:
>  		buf += buf [0];
>  	}
>  
> -	/* Microsoft ActiveSync based RNDIS devices lack the CDC descriptors,
> -	 * so we'll hard-wire the interfaces and not check for descriptors.
> +	/* Microsoft ActiveSync based and some regular RNDIS devices lack the
> +	 * CDC descriptors, so we'll hard-wire the interfaces and not check
> +	 * for descriptors.
>  	 */
> -	if (is_activesync(&intf->cur_altsetting->desc) && !info->u) {
> +	if (rndis && !info->u) {
>  		info->control = usb_ifnum_to_if(dev->udev, 0);
>  		info->data = usb_ifnum_to_if(dev->udev, 1);
>  		if (!info->control || !info->data) {
>  			dev_dbg(&intf->dev,
> -				"activesync: master #0/%p slave #1/%p\n",
> +				"rndis: master #0/%p slave #1/%p\n",
>  				info->control,
>  				info->data);
>  			goto bad_desc;
> @@ -316,7 +317,6 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
>  }
>  EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
>  
> -?
>  /*-------------------------------------------------------------------------
>   *
>   * Communications Device Class, Ethernet Control model
> -- 
> 1.5.2.5
>

^ permalink raw reply

* Re: [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind
From: David Brownell @ 2007-12-23  0:51 UTC (permalink / raw)
  To: bjd-a1rhEgazXTw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	jussi.kivilinna-E01nCVcF24I
In-Reply-To: <11983602953865-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>

> From bjd-a1rhEgazXTw@public.gmane.org  Sat Dec 22 13:53:03 2007
> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
> 	Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Subject: [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind
> Date: Sat, 22 Dec 2007 22:51:29 +0100
>
> From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
>
> rndis_command requires the caller to pass in a buffer of at least 1KB.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>

Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>

> ---
>  drivers/net/usb/rndis_host.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 96ef6a9..42b161c 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -577,7 +577,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
>  	struct rndis_halt	*halt;
>  
>  	/* try to clear any rndis state/activity (no i/o from stack!) */
> -	halt = kzalloc(sizeof *halt, GFP_KERNEL);
> +	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
>  	if (halt) {
>  		halt->msg_type = RNDIS_MSG_HALT;
>  		halt->msg_len = ccpu2(sizeof *halt);
> -- 
> 1.5.2.5
>

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox