Search Linux Wireless

[PATCH 2/4] b43legacy: fix upload of beacon packets to the hardware

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

 



This fixes uploading of the beacon data and writing of the TIM and DTIM offsets.

The patch by Michael Buesch has been ported to b43legacy.

Signed-off-by: Stefano Brivio <stefano.brivio@xxxxxxxxx>
---
Index: wireless-2.6/drivers/net/wireless/b43legacy/b43legacy.h
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/b43legacy/b43legacy.h
+++ wireless-2.6/drivers/net/wireless/b43legacy/b43legacy.h
@@ -126,13 +126,19 @@
 #define B43legacy_SHM_SH_HOSTFHI	0x0060 /* Hostflags ucode opts (high) */
 /* SHM_SHARED crypto engine */
 #define B43legacy_SHM_SH_KEYIDXBLOCK	0x05D4 /* Key index/algorithm block */
-/* SHM_SHARED beacon variables */
+/* SHM_SHARED beacon/AP variables */
+#define B43legacy_SHM_SH_DTIMP		0x0012 /* DTIM period */
+#define B43legacy_SHM_SH_BTL0		0x0018 /* Beacon template length 0 */
+#define B43legacy_SHM_SH_BTL1		0x001A /* Beacon template length 1 */
+#define B43legacy_SHM_SH_BTSFOFF	0x001C /* Beacon TSF offset */
+#define B43legacy_SHM_SH_TIMPOS		0x001E /* TIM position in beacon */
 #define B43legacy_SHM_SH_BEACPHYCTL	0x0054 /* Beacon PHY TX control word */
 /* SHM_SHARED ACK/CTS control */
 #define B43legacy_SHM_SH_ACKCTSPHYCTL	0x0022 /* ACK/CTS PHY control word */
 /* SHM_SHARED probe response variables */
-#define B43legacy_SHM_SH_PRPHYCTL	0x0188 /* Probe Resp PHY TX control */
+#define B43legacy_SHM_SH_PRTLEN		0x004A /* Probe Response template length */
 #define B43legacy_SHM_SH_PRMAXTIME	0x0074 /* Probe Response max time */
+#define B43legacy_SHM_SH_PRPHYCTL	0x0188 /* Probe Resp PHY TX control */
 /* SHM_SHARED rate tables */
 /* SHM_SHARED microcode soft registers */
 #define B43legacy_SHM_SH_UCODEREV	0x0000 /* Microcode revision */
@@ -597,6 +603,12 @@ struct b43legacy_wl {
 	u8 nr_devs;

 	bool radiotap_enabled;
+
+	/* The beacon we are currently using (AP or IBSS mode).
+	 * This beacon stuff is protected by the irq_lock. */
+	struct sk_buff *current_beacon;
+	bool beacon0_uploaded;
+	bool beacon1_uploaded;
 };

 /* Pointers to the firmware data and meta information about it. */
@@ -695,9 +707,6 @@ struct b43legacy_wldev {
 	u8 max_nr_keys;
 	struct b43legacy_key key[58];

-	/* Cached beacon template while uploading the template. */
-	struct sk_buff *cached_beacon;
-
 	/* Firmware data */
 	struct b43legacy_firmware fw;

Index: wireless-2.6/drivers/net/wireless/b43legacy/main.c
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/b43legacy/main.c
+++ wireless-2.6/drivers/net/wireless/b43legacy/main.c
@@ -960,16 +960,61 @@ static void b43legacy_write_beacon_templ
 					    u16 ram_offset,
 					    u16 shm_size_offset, u8 rate)
 {
-	int len;
-	const u8 *data;

-	B43legacy_WARN_ON(!dev->cached_beacon);
-	len = min((size_t)dev->cached_beacon->len,
+	unsigned int i, len, variable_len;
+	const struct ieee80211_mgmt *bcn;
+	const u8 *ie;
+	bool tim_found = 0;
+
+	bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
+	len = min((size_t)dev->wl->current_beacon->len,
 		  0x200 - sizeof(struct b43legacy_plcp_hdr6));
-	data = (const u8 *)(dev->cached_beacon->data);
-	b43legacy_write_template_common(dev, data,
-					len, ram_offset,
+
+	b43legacy_write_template_common(dev, (const u8 *)bcn, len, ram_offset,
 					shm_size_offset, rate);
+
+	/* Find the position of the TIM and the DTIM_period value
+	 * and write them to SHM. */
+	ie = bcn->u.beacon.variable;
+	variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+	for (i = 0; i < variable_len - 2; ) {
+		uint8_t ie_id, ie_len;
+
+		ie_id = ie[i];
+		ie_len = ie[i + 1];
+		if (ie_id == 5) {
+			u16 tim_position;
+			u16 dtim_period;
+			/* This is the TIM Information Element */
+
+			/* Check whether the ie_len is in the beacon data range. */
+			if (variable_len < ie_len + 2 + i)
+				break;
+			/* A valid TIM is at least 4 bytes long. */
+			if (ie_len < 4)
+				break;
+			tim_found = 1;
+
+			tim_position = sizeof(struct b43legacy_plcp_hdr6);
+			tim_position += offsetof(struct ieee80211_mgmt,
+						 u.beacon.variable);
+			tim_position += i;
+
+			dtim_period = ie[i + 3];
+
+			b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
+					B43legacy_SHM_SH_TIMPOS, tim_position);
+			b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
+					B43legacy_SHM_SH_DTIMP, dtim_period);
+			break;
+		}
+		i += ie_len + 2;
+	}
+	if (!tim_found) {
+		b43legacywarn(dev->wl, "Did not find a valid TIM IE in the "
+			      "beacon template packet. AP or IBSS operation "
+			      "may be broken.\n");
+	}
 }

 static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev,
@@ -1002,26 +1047,27 @@ static void b43legacy_write_probe_resp_p
  * 2) Patching duration field
  * 3) Stripping TIM
  */
-static u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
-					 u16 *dest_size,
-					 struct ieee80211_rate *rate)
+static const u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
+					       u16 *dest_size,
+					       struct ieee80211_rate *rate)
 {
 	const u8 *src_data;
 	u8 *dest_data;
-	u16 src_size;
-	u16 elem_size;
-	u16 src_pos;
-	u16 dest_pos;
+	u16 src_size, elem_size, src_pos, dest_pos;
 	__le16 dur;
 	struct ieee80211_hdr *hdr;
+	size_t ie_start;
+
+	src_size = dev->wl->current_beacon->len;
+	src_data = (const u8 *)dev->wl->current_beacon->data;

-	B43legacy_WARN_ON(!dev->cached_beacon);
-	src_size = dev->cached_beacon->len;
-	src_data = (const u8 *)dev->cached_beacon->data;
-
-	if (unlikely(src_size < 0x24)) {
-		b43legacydbg(dev->wl, "b43legacy_generate_probe_resp: "
-		       "invalid beacon\n");
+	/* Get the start offset of the variable IEs in the packet. */
+	ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+	B43legacy_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt,
+					       u.beacon.variable));
+
+	if (src_size < ie_start) {
+		B43legacy_WARN_ON(1);
 		return NULL;
 	}

@@ -1029,19 +1075,18 @@ static u8 *b43legacy_generate_probe_resp
 	if (unlikely(!dest_data))
 		return NULL;

-	/* 0x24 is offset of first variable-len Information-Element
-	 * in beacon frame.
-	 */
-	memcpy(dest_data, src_data, 0x24);
-	src_pos = 0x24;
-	dest_pos = 0x24;
-	for (; src_pos < src_size - 2; src_pos += elem_size) {
+	/* Copy the static data and all Information Elements, except the TIM. */
+	memcpy(dest_data, src_data, ie_start);
+	src_pos = ie_start;
+	dest_pos = ie_start;
+	for ( ; src_pos < src_size - 2; src_pos += elem_size) {
 		elem_size = src_data[src_pos + 1] + 2;
-		if (src_data[src_pos] != 0x05) { /* TIM */
-			memcpy(dest_data + dest_pos, src_data + src_pos,
-			       elem_size);
-			dest_pos += elem_size;
+		if (src_data[src_pos] == 5) {
+			/* This is the TIM. */
+			continue;
 		}
+		memcpy(dest_data + dest_pos, src_data + src_pos, elem_size);
+		dest_pos += elem_size;
 	}
 	*dest_size = dest_pos;
 	hdr = (struct ieee80211_hdr *)dest_data;
@@ -1063,11 +1108,10 @@ static void b43legacy_write_probe_resp_t
 						u16 shm_size_offset,
 						struct ieee80211_rate *rate)
 {
-	u8 *probe_resp_data;
+	const u8 *probe_resp_data;
 	u16 size;

-	B43legacy_WARN_ON(!dev->cached_beacon);
-	size = dev->cached_beacon->len;
+	size = dev->wl->current_beacon->len;
 	probe_resp_data = b43legacy_generate_probe_resp(dev, &size, rate);
 	if (unlikely(!probe_resp_data))
 		return;
@@ -1092,43 +1136,21 @@ static void b43legacy_write_probe_resp_t
 	kfree(probe_resp_data);
 }

-static int b43legacy_refresh_cached_beacon(struct b43legacy_wldev *dev,
-					   struct sk_buff *beacon)
-{
-	if (dev->cached_beacon)
-		kfree_skb(dev->cached_beacon);
-	dev->cached_beacon = beacon;
-
-	return 0;
-}
-
-static void b43legacy_update_templates(struct b43legacy_wldev *dev)
-{
-	u32 cmd;
-
-	B43legacy_WARN_ON(!dev->cached_beacon);
-
-	b43legacy_write_beacon_template(dev, 0x68, 0x18,
-					B43legacy_CCK_RATE_1MB);
-	b43legacy_write_beacon_template(dev, 0x468, 0x1A,
-					B43legacy_CCK_RATE_1MB);
-	b43legacy_write_probe_resp_template(dev, 0x268, 0x4A,
-					    &b43legacy_b_ratetable[0]);
-
-	cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
-	cmd |= B43legacy_MACCMD_BEACON0_VALID | B43legacy_MACCMD_BEACON1_VALID;
-	b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd);
-}
-
-static void b43legacy_refresh_templates(struct b43legacy_wldev *dev,
-					struct sk_buff *beacon)
-{
-	int err;
-
-	err = b43legacy_refresh_cached_beacon(dev, beacon);
-	if (unlikely(err))
-		return;
-	b43legacy_update_templates(dev);
+/* Asynchronously update the packet templates in template RAM.
+ * Locking: Requires wl->irq_lock to be locked. */
+static void b43legacy_update_templates(struct b43legacy_wl *wl,
+				       struct sk_buff *beacon)
+{
+	/* This is the top half of the ansynchronous beacon update. The bottom
+	 * half is the beacon IRQ. Beacon update must be asynchronous to avoid
+	 * sending an invalid beacon. This can happen for example, if the
+	 * firmware transmits a beacon while we are updating it. */
+
+	if (wl->current_beacon)
+		dev_kfree_skb_any(wl->current_beacon);
+	wl->current_beacon = beacon;
+	wl->beacon0_uploaded = 0;
+	wl->beacon1_uploaded = 0;
 }

 static void b43legacy_set_ssid(struct b43legacy_wldev *dev,
@@ -1169,38 +1191,37 @@ static void b43legacy_set_beacon_int(str

 static void handle_irq_beacon(struct b43legacy_wldev *dev)
 {
-	u32 status;
+	struct b43legacy_wl *wl = dev->wl;
+	u32 cmd;

-	if (!b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_AP))
+	if (!b43legacy_is_mode(wl, IEEE80211_IF_TYPE_AP))
 		return;

-	dev->irq_savedstate &= ~B43legacy_IRQ_BEACON;
-	status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
+	/* This is the bottom half of the asynchronous beacon update. */

-	if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) {
-		/* ACK beacon IRQ. */
-		b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON,
-				  B43legacy_IRQ_BEACON);
-		dev->irq_savedstate |= B43legacy_IRQ_BEACON;
-		if (dev->cached_beacon)
-			kfree_skb(dev->cached_beacon);
-		dev->cached_beacon = NULL;
-		return;
-	}
-	if (!(status & 0x1)) {
-		b43legacy_write_beacon_template(dev, 0x68, 0x18,
-						B43legacy_CCK_RATE_1MB);
-		status |= 0x1;
-		b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
-				  status);
-	}
-	if (!(status & 0x2)) {
-		b43legacy_write_beacon_template(dev, 0x468, 0x1A,
-						B43legacy_CCK_RATE_1MB);
-		status |= 0x2;
-		b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
-				  status);
+	cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
+	if (!(cmd & B43legacy_MACCMD_BEACON0_VALID)) {
+		if (!wl->beacon0_uploaded) {
+			b43legacy_write_beacon_template(dev, 0x68,
+							B43legacy_SHM_SH_BTL0,
+							B43legacy_CCK_RATE_1MB);
+			b43legacy_write_probe_resp_template(dev, 0x268,
+							    B43legacy_SHM_SH_PRTLEN,
+							    &__b43legacy_ratetable[3]);
+			wl->beacon0_uploaded = 1;
+		}
+		cmd |= B43legacy_MACCMD_BEACON0_VALID;
+	}
+	if (!(cmd & B43legacy_MACCMD_BEACON1_VALID)) {
+		if (!wl->beacon1_uploaded) {
+			b43legacy_write_beacon_template(dev, 0x468,
+							B43legacy_SHM_SH_BTL1,
+							B43legacy_CCK_RATE_1MB);
+			wl->beacon1_uploaded = 1;
+		}
+		cmd |= B43legacy_MACCMD_BEACON1_VALID;
 	}
+	b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd);
 }

 static void handle_irq_ucode_debug(struct b43legacy_wldev *dev)
@@ -2706,7 +2727,7 @@ static int b43legacy_op_config_interface
 			B43legacy_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP);
 			b43legacy_set_ssid(dev, conf->ssid, conf->ssid_len);
 			if (conf->beacon)
-				b43legacy_refresh_templates(dev, conf->beacon);
+				b43legacy_update_templates(wl, conf->beacon);
 		}
 		b43legacy_write_mac_bssid_templates(dev);
 	}
@@ -3019,6 +3040,11 @@ static void b43legacy_wireless_core_exit
 		kfree(phy->tssi2dbm);
 	kfree(phy->lo_control);
 	phy->lo_control = NULL;
+	if (dev->wl->current_beacon) {
+		dev_kfree_skb_any(dev->wl->current_beacon);
+		dev->wl->current_beacon = NULL;
+	}
+
 	ssb_device_disable(dev->dev, 0);
 	ssb_bus_may_powerdown(dev->dev->bus);
 }
@@ -3343,6 +3369,41 @@ out_unlock:
 	return err;
 }

+static int b43legacy_op_beacon_set_tim(struct ieee80211_hw *hw,
+				       int aid, int set)
+{
+	struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
+	struct sk_buff *beacon;
+	unsigned long flags;
+
+	/* We could modify the existing beacon and set the aid bit in the TIM
+	 * field, but that would probably require resizing and moving of data
+	 * within the beacon template. Simply request a new beacon and let
+	 * mac80211 do the hard work. */
+	beacon = ieee80211_beacon_get(hw, wl->vif, NULL);
+	if (unlikely(!beacon))
+		return -ENOMEM;
+	spin_lock_irqsave(&wl->irq_lock, flags);
+	b43legacy_update_templates(wl, beacon);
+	spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+	return 0;
+}
+
+static int b43legacy_op_ibss_beacon_update(struct ieee80211_hw *hw,
+					   struct sk_buff *beacon,
+					   struct ieee80211_tx_control *ctl)
+{
+	struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wl->irq_lock, flags);
+	b43legacy_update_templates(wl, beacon);
+	spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+	return 0;
+}
+
 static const struct ieee80211_ops b43legacy_hw_ops = {
 	.tx			= b43legacy_op_tx,
 	.conf_tx		= b43legacy_op_conf_tx,
@@ -3356,6 +3417,8 @@ static const struct ieee80211_ops b43leg
 	.start			= b43legacy_op_start,
 	.stop			= b43legacy_op_stop,
 	.set_retry_limit	= b43legacy_op_set_retry_limit,
+	.set_tim		= b43legacy_op_beacon_set_tim,
+	.beacon_update		= b43legacy_op_ibss_beacon_update,
 };

 /* Hard-reset the chip. Do not call this directly.

-- 
Ciao
Stefano
-
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