Subject: [PATCH][RFC] softmac: suggest TX rate

From: Daniel Drake <[email-masked]>
Date: Mon, 17 Apr 2006 01:28:46 +0100

While developing the ZD1211 driver we realised how much we'd like to be 
advised from the upper layers which rate to transmit a packet at.

An example: We have a frame to transmit. What rate should we transmit it 
at? While taking any user-specified rate into account too, we want to 
transmit it at a rate supported by the access point, but that 
information is not available to us (softmac handled all scanning and 
association, so we don't know anything about the AP capabilities).

Here is a patch I cooked up, which implements some basic logic for 
suggesting TX rates based on the packet type, whether it is 
multicast/unicast, whether we are associated, and the current 
user-specified default_rate.

I'm still uncertain where this functionality should fit into the stack.

Rather than having to explicitly call ieee80211softmac_suggest_tx_rate() 
from the driver's hard_start_xmit function, it would be nicer if the 
suggested rate was passed as a parameter. But not all drivers would need 
it, so maybe the extra calculations should be controlled by a new flag.

Passing an extra parameter to hard_start_xmit would involve modifying 
ieee80211, and ieee80211 doesn't seem to have any concept of whether it 
is associated or not (plus softmac only does that loosely). So that 
might lead us on to do something silly like wrapping hard_start_xmit 
inside softmac, etc etc...

Note that even in it's current form, this patch eliminates an annoying 
(and inaccurate) chunk of code from our driver.

Ideas/comments?
--- linux/net/ieee80211/softmac/ieee80211softmac_module.c.orig	2006-04-17 01:04:42.000000000 +0100
+++ linux/net/ieee80211/softmac/ieee80211softmac_module.c	2006-04-17 00:44:36.000000000 +0100
@@ -26,6 +26,7 @@
 
 #include "ieee80211softmac_priv.h"
 #include <linux/sort.h>
+#include <linux/etherdevice.h>
 
 struct net_device *alloc_ieee80211softmac(int sizeof_priv)
 {
@@ -315,6 +316,48 @@ static int rate_cmp(const void *a_, cons
 	return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
 }
 
+static u8 suggest_rate_from_associnfo(struct ieee80211softmac_device *mac)
+{
+	struct ieee80211softmac_ratesinfo *net_rates = &mac->associnfo.supported_rates;
+	u8 net_max;
+
+	if (unlikely(net_rates->count == 0)) {
+		dprintkl(KERN_ERROR PFX "suggest_rate: Network has no rates?\n");
+		return IEEE80211_CCK_RATE_1MB;
+	}
+
+	/* FIXME: we need to check that the rate is supported in mac->ratesinfo */
+	net_max = net_rates->rates[net_rates->count - 1];
+	return min(mac->txrates.default_rate, net_max);
+}
+
+u8 ieee80211softmac_suggest_tx_rate(struct ieee80211softmac_device *mac,
+	struct ieee80211_hdr_1addr *hdr)
+{
+	switch (WLAN_FC_GET_TYPE(le16_to_cpu(hdr->frame_ctl))) {
+	case IEEE80211_FTYPE_MGMT:
+		/*
+		 * If we aren't associated, or we are multicasting, then
+		 * stick to 1MB for safety.
+		 */
+		if (!mac->associated || is_multicast_ether_addr(hdr->addr1))
+			return IEEE80211_CCK_RATE_1MB;
+
+		/* Otherwise, we can send at the speed of the AP. */
+		return suggest_rate_from_associnfo(mac);
+	case IEEE80211_FTYPE_DATA:
+		if (unlikely(!mac->associated)) {
+			dprintkl(KERN_ERROR PFX "suggest_tx_rate: Not associated\n");
+			return IEEE80211_CCK_RATE_1MB;
+		}
+		return suggest_rate_from_associnfo(mac);
+	default:
+		dprintkl(KERN_ERROR PFX "suggest_tx_rate: Unhandled ftype %x\n", ftype);
+		return IEEE80211_CCK_RATE_1MB;
+	}
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_suggest_tx_rate);
+
 /* Allocate a softmac network struct and fill it from a network */
 struct ieee80211softmac_network *
 ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
--- linux/net/ieee80211/softmac/ieee80211softmac_assoc.c.orig	2006-04-16 23:55:23.000000000 +0100
+++ linux/net/ieee80211/softmac/ieee80211softmac_assoc.c	2006-04-16 23:55:39.000000000 +0100
@@ -283,6 +283,7 @@ ieee80211softmac_associated(struct ieee8
 	struct ieee80211softmac_network *net)
 {
 	mac->associnfo.associating = 0;
+	mac->associnfo.supported_rates = net->supported_rates;
 	mac->associated = 1;
 	if (mac->set_bssid_filter)
 		mac->set_bssid_filter(mac->dev, net->bssid);
--- linux/include/net/ieee80211softmac.h.orig	2006-04-16 23:37:32.000000000 +0100
+++ linux/include/net/ieee80211softmac.h	2006-04-17 00:44:47.000000000 +0100
@@ -86,6 +86,9 @@ struct ieee80211softmac_assoc_info {
 	
 	/* BSSID we're trying to associate to */
 	char bssid[ETH_ALEN];
+
+	/* Rates supported by the network */
+	struct ieee80211softmac_ratesinfo supported_rates;
 	
 	/* some flags.
 	 * static_essid is valid if the essid is constant,
@@ -246,6 +249,8 @@ extern void ieee80211softmac_fragment_lo
  * Note that the rates need to be sorted. */
 extern void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates);
 
+extern u8 ieee80211softmac_suggest_tx_rate(struct ieee80211softmac_device *mac, struct ieee80211_hdr_1addr *hdr);
+
 /* Start the SoftMAC. Call this after you initialized the device
  * and it is ready to run.
  */