Search Linux Wireless

[RFC] b43: multiple MAC addresses support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds support for multiple virtual interfaces (up to 65536)
to b43. This requires modified firmware which is detected by an invalid
day in the firmware timestamp (254), and then the firmware time and the
rest of the date is used for feature bits.

The multi-MAC support works by having a mask of ignored bits, the
modified firmware will mask off these bits before comparing the address
on incoming frames with the address. This, however, means that when you
add three MAC virtual interfaces, the firmware will at least also ACK
frames on a fourth address. The required mask is calculated dynamically
to be the least restrictive mask possible, but you should take care to
allocate your MAC addresses in a way that minimises the mask to avoid
strange behaviour. For example, avoid using addresses 02:00:00:00:00:02
and 02:00:00:00:00:10 together because these two have only 46 bits in
common, use 02:00:00:00:00:12 and 02:00:00:00:00:10 instead as those
have 47 bits in common.

Note that the mask applies only to the lower 16 bits, so the first four
bytes of all addresses need to be identical.

The required tool to patch the firmware will be published separately.

Signed-off-by: Johannes Berg <johannes@xxxxxxxxxxxxxxxx>
Cc: Michael Wu <flamingice@xxxxxxxxxxxx>
Cc: Jiri Benc <jbenc@xxxxxxx>
Cc: Ben Greear <greearb@xxxxxxxxxxxxxxx>
---
 drivers/net/wireless/b43/b43.h  |   19 ++-
 drivers/net/wireless/b43/main.c |  200 +++++++++++++++++++++++++++++++++-------
 drivers/net/wireless/b43/xmit.c |    6 -
 3 files changed, 180 insertions(+), 45 deletions(-)

--- everything.orig/drivers/net/wireless/b43/main.c	2007-11-09 01:53:47.158996039 +0100
+++ everything/drivers/net/wireless/b43/main.c	2007-11-09 02:04:31.358978135 +0100
@@ -543,7 +543,21 @@ static void b43_write_mac_bssid_template
 static void b43_upload_card_macaddress(struct b43_wldev *dev)
 {
 	b43_write_mac_bssid_templates(dev);
-	b43_macfilter_set(dev, B43_MACFILTER_SELF, dev->wl->mac_addr);
+
+	if (dev->fw.features & B43_FW_FEATURE_MULTI_MAC_STA) {
+		u16 tmp;
+
+		tmp = ((u16 *)dev->wl->mac_addr)[0];
+		b43_shm_write16(dev, B43_SHM_SHARED, 0x320, cpu_to_le16(tmp));
+		tmp = ((u16 *)dev->wl->mac_addr)[1];
+		b43_shm_write16(dev, B43_SHM_SHARED, 0x322, cpu_to_le16(tmp));
+		tmp = ((u16 *)dev->wl->mac_addr)[2];
+		b43_shm_write16(dev, B43_SHM_SHARED, 0x324, cpu_to_le16(tmp));
+		/* swap here because we always treat the mask as big endian */
+		b43_shm_write16(dev, B43_SHM_SHARED, 0x326,
+				swab16(dev->wl->mac_mask));
+	} else
+		b43_macfilter_set(dev, B43_MACFILTER_SELF, dev->wl->mac_addr);
 }
 
 static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time)
@@ -1174,18 +1188,18 @@ static void b43_write_probe_resp_plcp(st
 {
 	struct b43_plcp_hdr4 plcp;
 	u32 tmp;
-	__le16 dur;
 
 	plcp.data = 0;
 	b43_generate_plcp_hdr(&plcp, size + FCS_LEN, rate);
-	dur = ieee80211_generic_frame_duration(dev->wl->hw,
-					       dev->wl->if_id, size,
-					       B43_RATE_TO_BASE100KBPS(rate));
 	/* Write PLCP in two parts and timing for packet transfer */
 	tmp = le32_to_cpu(plcp.data);
 	b43_shm_write16(dev, B43_SHM_SHARED, shm_offset, tmp & 0xFFFF);
 	b43_shm_write16(dev, B43_SHM_SHARED, shm_offset + 2, tmp >> 16);
-	b43_shm_write16(dev, B43_SHM_SHARED, shm_offset + 6, le16_to_cpu(dur));
+	/*
+	 * XXX: currently, the duration here doesn't matter, but
+	 * when we implement offload it needs to be fixed!
+	 */
+	b43_shm_write16(dev, B43_SHM_SHARED, shm_offset + 6, 0);
 }
 
 /* Instead of using custom probe response template, this function
@@ -1200,7 +1214,6 @@ static u8 *b43_generate_probe_resp(struc
 	const u8 *src_data;
 	u8 *dest_data;
 	u16 src_size, elem_size, src_pos, dest_pos;
-	__le16 dur;
 	struct ieee80211_hdr *hdr;
 
 	B43_WARN_ON(!dev->cached_beacon);
@@ -1235,10 +1248,11 @@ static u8 *b43_generate_probe_resp(struc
 	/* Set the frame control. */
 	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
 					 IEEE80211_STYPE_PROBE_RESP);
-	dur = ieee80211_generic_frame_duration(dev->wl->hw,
-					       dev->wl->if_id, *dest_size,
-					       B43_RATE_TO_BASE100KBPS(rate));
-	hdr->duration_id = dur;
+	/*
+	 * XXX: currently, the duration here doesn't matter, but
+	 * when we implement offload it needs to be fixed!
+	 */
+	hdr->duration_id = 0;
 
 	return dest_data;
 }
@@ -1792,11 +1806,23 @@ static int b43_upload_microcode(struct b
 		err = -EOPNOTSUPP;
 		goto out;
 	}
-	b43dbg(dev->wl, "Loading firmware version %u.%u "
-	       "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n",
-	       fwrev, fwpatch,
-	       (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF,
-	       (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F);
+	/* special day 254: modified firmware with feature bits */
+	if ((fwdate & 0xFF) == 0xFE) {
+		b43dbg(dev->wl, "Loading firmware version %u.%u\n",
+		       fwrev, fwpatch);
+		dev->fw.features = fwtime | ((fwdate & 0xFF00) << 8);
+		b43dbg(dev->wl, "This firmware was modified and has the"
+				" added features 0x%06x\n", dev->fw.features);
+		if (dev->fw.features & B43_FW_FEATURE_MULTI_MAC_STA)
+			b43dbg(dev->wl, " * station multi MAC addr support\n");
+	} else
+		b43dbg(dev->wl, "Loading firmware version %u.%u "
+		       "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n",
+		       fwrev, fwpatch,
+		       (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF,
+		       fwdate & 0xFF,
+		       (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F,
+		       fwtime & 0x1F);
 
 	dev->fw.rev = fwrev;
 	dev->fw.patch = fwpatch;
@@ -2948,11 +2974,18 @@ static int b43_op_config_interface(struc
 		return -ENODEV;
 	mutex_lock(&wl->mutex);
 	spin_lock_irqsave(&wl->irq_lock, flags);
-	B43_WARN_ON(wl->if_id != if_id);
-	if (conf->bssid)
-		memcpy(wl->bssid, conf->bssid, ETH_ALEN);
-	else
-		memset(wl->bssid, 0, ETH_ALEN);
+
+	/*
+	 * Only take bssid of first iface, we can only adopt time from
+	 * one BSS so arbitrarily choose the first.
+	 */
+	if (wl->if_id == if_id) {
+		if (conf->bssid)
+			memcpy(wl->bssid, conf->bssid, ETH_ALEN);
+		else
+			memset(wl->bssid, 0, ETH_ALEN);
+	}
+
 	if (b43_status(dev) >= B43_STAT_INITIALIZED) {
 		if (b43_is_mode(wl, IEEE80211_IF_TYPE_AP)) {
 			B43_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP);
@@ -3424,6 +3457,71 @@ static int b43_wireless_core_init(struct
 	return err;
 }
 
+struct mac_iter {
+	struct b43_wl *wl;
+	u16 mac_val;
+	bool add;
+	int count;
+};
+
+static void b43_mac_iter(void *data, u8 *mac, int if_id)
+{
+	struct mac_iter *iter = data;
+	u16 val = (mac[4] << 8) | mac[5];
+	u16 cur = (iter->wl->mac_addr[4] << 8) | iter->wl->mac_addr[5];
+
+	/* skip the one that's being removed */
+	if (!iter->add && val == iter->mac_val)
+		return;
+
+	/* take first address we find as base */
+	if (iter->count == 0) {
+		cur = val;
+		iter->wl->mac_addr[4] = cur >> 8;
+		iter->wl->mac_addr[5] = cur;
+	}
+
+	iter->wl->mac_mask |= cur ^ val;
+
+	iter->count++;
+}
+
+static void b43_adjust_mac_addr_mask(struct b43_wl *wl, bool add, u8 *addr)
+{
+	struct mac_iter iter;
+	DECLARE_MAC_BUF(mbuf);
+	u16 cur;
+	u16 val = (addr[4] << 8) | addr[5];
+
+	/*
+	 * Because this is not invoked on adding the first address,
+	 * we do not need to have any special code for that case.
+	 * If it was invoked, we'd have to set cur to val here etc.
+	 * like above in the iter->count == 0 case (because we don't
+	 * see the interface that's being added in the iteration.)
+	 */
+
+	wl->mac_mask = 0;
+
+	iter.wl = wl;
+	iter.add = add;
+	iter.mac_val = val;
+	iter.count = 0;
+	ieee80211_iterate_active_interfaces(wl->hw, b43_mac_iter, &iter);
+
+	/* add the one that's being added */
+	cur = (wl->mac_addr[4] << 8) | wl->mac_addr[5];
+	if (add)
+		wl->mac_mask |= cur ^ val;
+
+	cur = cur & ~wl->mac_mask;
+	wl->mac_addr[4] = cur >> 8;
+	wl->mac_addr[5] = cur;
+
+	b43dbg(wl, "MAC address is %s, mask is 0x%.4x\n",
+	       print_mac(mbuf, wl->mac_addr), wl->mac_mask);
+}
+
 static int b43_op_add_interface(struct ieee80211_hw *hw,
 				struct ieee80211_if_init_conf *conf)
 {
@@ -3441,16 +3539,36 @@ static int b43_op_add_interface(struct i
 		return -EOPNOTSUPP;
 
 	mutex_lock(&wl->mutex);
-	if (wl->operating)
-		goto out_mutex_unlock;
+	dev = wl->current_dev;
 
-	b43dbg(wl, "Adding Interface type %d\n", conf->type);
+	if (dev->fw.features & B43_FW_FEATURE_MULTI_MAC_STA) {
+		if (wl->if_count && conf->type != IEEE80211_IF_TYPE_STA)
+			goto out_mutex_unlock;
+		if (wl->if_count > 0) {
+			if (((u8 *)conf->mac_addr)[0] != wl->mac_addr[0])
+				goto out_mutex_unlock;
+			if (((u8 *)conf->mac_addr)[1] != wl->mac_addr[1])
+				goto out_mutex_unlock;
+			if (((u8 *)conf->mac_addr)[2] != wl->mac_addr[2])
+				goto out_mutex_unlock;
+			if (((u8 *)conf->mac_addr)[3] != wl->mac_addr[3])
+				goto out_mutex_unlock;
+			b43_adjust_mac_addr_mask(wl, 1, conf->mac_addr);
+		}
+	} else {
+		if (wl->if_count)
+			goto out_mutex_unlock;
+	}
 
-	dev = wl->current_dev;
-	wl->operating = 1;
-	wl->if_id = conf->if_id;
-	wl->if_type = conf->type;
-	memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
+	wl->if_count++;
+
+	if (wl->if_count == 1) {
+		wl->if_id = conf->if_id;
+		wl->if_type = conf->type;
+		memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
+	}
+
+	b43dbg(wl, "Adding Interface type %d\n", conf->type);
 
 	spin_lock_irqsave(&wl->irq_lock, flags);
 	b43_adjust_opmode(dev);
@@ -3464,6 +3582,14 @@ static int b43_op_add_interface(struct i
 	return err;
 }
 
+static void b43_ifid_iter(void *data, u8 *mac, int if_id)
+{
+	struct b43_wl *wl = data;
+
+	if (wl->if_id != if_id)
+		wl->if_id = if_id;
+}
+
 static void b43_op_remove_interface(struct ieee80211_hw *hw,
 				    struct ieee80211_if_init_conf *conf)
 {
@@ -3475,14 +3601,22 @@ static void b43_op_remove_interface(stru
 
 	mutex_lock(&wl->mutex);
 
-	B43_WARN_ON(!wl->operating);
-	B43_WARN_ON(wl->if_id != conf->if_id);
+	B43_WARN_ON(!wl->if_count);
+
+	wl->if_count--;
+	if (wl->if_count == 0) {
+		wl->if_type = IEEE80211_IF_TYPE_INVALID;
+		memset(wl->mac_addr, 0, ETH_ALEN);
+	}
+
+	/* arbitrarily adopt another if_id as main if_id */
+	if (conf->if_id == wl->if_id)
+		ieee80211_iterate_active_interfaces(wl->hw, b43_ifid_iter, wl);
 
-	wl->operating = 0;
+	b43_adjust_mac_addr_mask(wl, 0, conf->mac_addr);
 
 	spin_lock_irqsave(&wl->irq_lock, flags);
 	b43_adjust_opmode(dev);
-	memset(wl->mac_addr, 0, ETH_ALEN);
 	b43_upload_card_macaddress(dev);
 	spin_unlock_irqrestore(&wl->irq_lock, flags);
 
--- everything.orig/drivers/net/wireless/b43/b43.h	2007-11-09 01:53:47.248981228 +0100
+++ everything/drivers/net/wireless/b43/b43.h	2007-11-09 02:06:41.818973253 +0100
@@ -601,22 +601,20 @@ struct b43_wl {
 	struct mutex mutex;
 	spinlock_t leds_lock;
 
-	/* We can only have one operating interface (802.11 core)
-	 * at a time. General information about this interface follows.
-	 */
-
-	/* Opaque ID of the operating interface from the ieee80211
+	/*
+	 * Opaque ID of the first operating interface from the ieee80211
 	 * subsystem. Do not modify.
 	 */
 	int if_id;
-	/* The MAC address of the operating interface. */
+	/* The MAC address */
 	u8 mac_addr[ETH_ALEN];
+	u16 mac_mask;
 	/* Current BSSID */
 	u8 bssid[ETH_ALEN];
 	/* Interface type. (IEEE80211_IF_TYPE_XXX) */
 	int if_type;
-	/* Is the card operating in AP, STA or IBSS mode? */
-	bool operating;
+	/* number of open interfaces */
+	int if_count;
 	/* filter flags */
 	unsigned int filter_flags;
 	/* Stats about the wireless interface */
@@ -648,6 +646,9 @@ struct b43_firmware {
 	u16 rev;
 	/* Firmware patchlevel */
 	u16 patch;
+	/* Firmware features (24 bits) */
+#define B43_FW_FEATURE_MULTI_MAC_STA	0x000001
+	u32 features;
 };
 
 /* Device (802.11 core) initialization status. */
@@ -781,7 +782,7 @@ static inline struct b43_wldev *dev_to_b
 /* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */
 static inline int b43_is_mode(struct b43_wl *wl, int type)
 {
-	return (wl->operating && wl->if_type == type);
+	return (wl->if_count && wl->if_type == type);
 }
 
 static inline u16 b43_read16(struct b43_wldev *dev, u16 offset)
--- everything.orig/drivers/net/wireless/b43/xmit.c	2007-11-09 01:53:47.378984104 +0100
+++ everything/drivers/net/wireless/b43/xmit.c	2007-11-09 01:53:57.698983181 +0100
@@ -221,7 +221,7 @@ static void generate_txhdr_fw4(struct b4
 	} else {
 		int fbrate_base100kbps = B43_RATE_TO_BASE100KBPS(rate_fb);
 		txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw,
-								 dev->wl->if_id,
+								 txctl->ifindex,
 								 fragment_len,
 								 fbrate_base100kbps);
 	}
@@ -311,7 +311,7 @@ static void generate_txhdr_fw4(struct b4
 		rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb);
 
 		if (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
-			ieee80211_ctstoself_get(dev->wl->hw, dev->wl->if_id,
+			ieee80211_ctstoself_get(dev->wl->hw, txctl->ifindex,
 						fragment_data, fragment_len,
 						txctl,
 						(struct ieee80211_cts *)(txhdr->
@@ -319,7 +319,7 @@ static void generate_txhdr_fw4(struct b4
 			mac_ctl |= B43_TX4_MAC_SENDCTS;
 			len = sizeof(struct ieee80211_cts);
 		} else {
-			ieee80211_rts_get(dev->wl->hw, dev->wl->if_id,
+			ieee80211_rts_get(dev->wl->hw, txctl->ifindex,
 					  fragment_data, fragment_len, txctl,
 					  (struct ieee80211_rts *)(txhdr->
 								   rts_frame));


-
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux