All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michael Buesch <mb@bu3sch.de>
To: Jiri Benc <jbenc@suse.cz>
Cc: "John W. Linville" <linville@tuxdriver.com>,
	bcm43xx-dev@lists.berlios.de, netdev@vger.kernel.org
Subject: [PATCH] bcm43xx-d80211: proper implementation of virtual interface support
Date: Mon, 1 May 2006 21:35:00 +0200	[thread overview]
Message-ID: <200605012135.00895.mb@bu3sch.de> (raw)

[-- Attachment #1: Type: text/plain, Size: 15995 bytes --]

Hi,

Jiri, please review this patch for things that might look
strange to you. :)

--

This replaces the bcm43xx-d80211 virtual interfaces hack by
a correct implementation with support for monitor during oper.

Index: wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h
===================================================================
--- wireless-dev.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h	2006-04-28 16:13:40.000000000 +0200
+++ wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h	2006-05-01 20:25:31.000000000 +0200
@@ -626,10 +626,34 @@
 	u8 algorithm;
 };
 
+struct bcm43xx_interface {
+	struct list_head list;
+	/* Interface type (IEEE80211_IF_TYPE_XXX). */
+	int type;
+	/* Opaque ID from the ieee80211 subsystem. Do not modify. */
+	int if_id;
+	/* MAC address for this interface. */
+	u8 *mac_addr;
+	/* BSSID (if any). */
+	u8 *bssid;
+};
+
+struct bcm43xx_interface_list {
+	/* Linked list of active interfaces. */
+	struct list_head list;
+	/* Shortcut pointer to the AP interface (if any). */
+	struct bcm43xx_interface *ap_if;
+
+	/* Usage counters of the internal operation modes. */
+	u16 opmode_ap;
+	u16 opmode_adhoc;
+	u16 opmode_monitor;
+	u16 opmode_promisc;
+};
+
 struct bcm43xx_private {
 	struct ieee80211_hw *ieee;
 	struct ieee80211_low_level_stats ieee_stats;
-	int iw_mode;
 
 	struct net_device *net_dev;
 	struct pci_dev *pci_dev;
@@ -653,6 +677,11 @@
 	    short_slot:1,		/* TRUE, if short slot timing is enabled. */
 	    firmware_norelease:1;	/* Do not release the firmware. Used on suspend. */
 
+	/* One physical device can have one or more virtual
+	 * interfaces. This stores and manages the virtual interfaces.
+	 */
+	struct bcm43xx_interface_list interfaces;
+	/* Various statistics about the physical device. */
 	struct bcm43xx_stats stats;
 
 	/* Bus type we are connected to.
@@ -716,8 +745,6 @@
 
 	/* Informational stuff. */
 	char nick[IW_ESSID_MAX_SIZE + 1];
-	u8 bssid[ETH_ALEN];
-	int interfaces;
 
 	/* encryption/decryption */
 	u16 security_offset;
Index: wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c
===================================================================
--- wireless-dev.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c	2006-04-28 16:13:40.000000000 +0200
+++ wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c	2006-05-01 18:57:26.000000000 +0200
@@ -217,7 +217,7 @@
 				bcm43xx_led_blink_stop(led, 0);
 			continue;
 		case BCM43xx_LED_APTRANSFER:
-			if (bcm->iw_mode == IW_MODE_MASTER) {
+			if (bcm->interfaces.opmode_ap) {
 				if (transferring) {
 					interval = BCM43xx_LEDBLINK_FAST;
 					turn_on = 1;
Index: wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
===================================================================
--- wireless-dev.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c	2006-04-28 16:13:40.000000000 +0200
+++ wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c	2006-05-01 20:25:24.000000000 +0200
@@ -378,18 +378,30 @@
 static void bcm43xx_macfilter_clear(struct bcm43xx_private *bcm,
 				    u16 offset)
 {
-	const u8 zero_addr[ETH_ALEN] = { 0 };
+	static const u8 zero_addr[ETH_ALEN] = { 0 };
 
 	bcm43xx_macfilter_set(bcm, offset, zero_addr);
 }
 
 static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_private *bcm)
 {
+	static const u8 zero_addr[ETH_ALEN] = { 0 };
+	struct bcm43xx_interface *iface;
 	const u8 *mac = (const u8 *)(bcm->net_dev->dev_addr);
-	const u8 *bssid = bcm->bssid;
+	const u8 *bssid = NULL;
 	u8 mac_bssid[ETH_ALEN * 2];
 	int i;
 
+	list_for_each_entry(iface, &bcm->interfaces.list, list) {
+		if (iface->type != IEEE80211_IF_TYPE_MNTR &&
+		    iface->bssid) {
+			bssid = iface->bssid;
+			break;
+		}
+	}
+	if (!bssid)
+		bssid = zero_addr;
+
 	memcpy(mac_bssid, mac, ETH_ALEN);
 	memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN);
 
@@ -1438,15 +1450,15 @@
 
 static void handle_irq_ps(struct bcm43xx_private *bcm)
 {
-	if (bcm->iw_mode == IW_MODE_MASTER) {
+	if (bcm->interfaces.opmode_ap) {
 		///TODO: PS TBTT
 	} else {
 		if (1/*FIXME: the last PSpoll frame was sent successfully */)
 			bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
 	}
-	if (bcm->iw_mode == IW_MODE_ADHOC)
+	bcm->reg124_set_0x4 = 0;
+	if (bcm->interfaces.opmode_adhoc)
 		bcm->reg124_set_0x4 = 1;
-	//FIXME else set to false?
 }
 
 static void handle_irq_reg124(struct bcm43xx_private *bcm)
@@ -1456,7 +1468,6 @@
 	bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD,
 			bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD)
 			| 0x4);
-	//FIXME: reset reg124_set_0x4 to false?
 }
 
 static void handle_irq_pmq(struct bcm43xx_private *bcm)
@@ -1509,7 +1520,10 @@
 		 * Request the 80211 subsystem to generate a new beacon
 		 * frame and use it as template.
 		 */
-		bcm->cached_beacon = ieee80211_beacon_get(bcm->net_dev, 0, &control);
+		assert(bcm->interfaces.ap_if);
+		bcm->cached_beacon = ieee80211_beacon_get(bcm->net_dev,
+							  bcm->interfaces.ap_if->if_id,
+							  &control);
 		if (unlikely(!bcm->cached_beacon)) {
 			dprintkl(KERN_WARNING PFX "Could not generate beacon template.\n");
 			goto ack;
@@ -1603,7 +1617,7 @@
 	}
 
 	if (reason & BCM43xx_IRQ_BEACON) {
-		if (bcm->iw_mode == IW_MODE_MASTER)
+		if (bcm->interfaces.opmode_ap)
 			handle_irq_beacon(bcm);
 		bcmirq_handled(BCM43xx_IRQ_BEACON);
 	}
@@ -2147,55 +2161,35 @@
 	printkl(KERN_ERR PFX "MAC suspend failed\n");
 }
 
-void bcm43xx_set_iwmode(struct bcm43xx_private *bcm,
-			int iw_mode)
+static void bcm43xx_select_opmodes(struct bcm43xx_private *bcm)
 {
-	struct net_device *net_dev = bcm->net_dev;
 	u32 status;
 	u16 value;
 
-	bcm->iw_mode = iw_mode;
-	if (iw_mode == IW_MODE_MONITOR)
-		net_dev->type = ARPHRD_IEEE80211;
-	else
-		net_dev->type = ARPHRD_ETHER;
-
 	status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
-	/* Reset status to infrastructured mode */
-	status &= ~(BCM43xx_SBF_MODE_AP | BCM43xx_SBF_MODE_MONITOR);
+	/* Reset status to default STA mode */
+	status &= ~BCM43xx_SBF_MODE_AP;
+	status &= ~BCM43xx_SBF_MODE_MONITOR;
 	status &= ~BCM43xx_SBF_MODE_PROMISC;
 	status |= BCM43xx_SBF_MODE_NOTADHOC;
 
+	if (bcm->interfaces.opmode_ap)
+		status |= BCM43xx_SBF_MODE_AP;
+	if (bcm->interfaces.opmode_adhoc)
+		status &= ~BCM43xx_SBF_MODE_NOTADHOC;
+	if (bcm->interfaces.opmode_monitor)
+		status |= BCM43xx_SBF_MODE_MONITOR;
+	if (bcm->interfaces.opmode_promisc)
+		status |= BCM43xx_SBF_MODE_PROMISC;
+
 /* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */
 status |= BCM43xx_SBF_MODE_PROMISC;
 
-	switch (iw_mode) {
-	case IW_MODE_MONITOR:
-		status |= BCM43xx_SBF_MODE_MONITOR;
-		status |= BCM43xx_SBF_MODE_PROMISC;
-		break;
-	case IW_MODE_ADHOC:
-		status &= ~BCM43xx_SBF_MODE_NOTADHOC;
-		break;
-	case IW_MODE_MASTER:
-		status |= BCM43xx_SBF_MODE_AP;
-		break;
-	case IW_MODE_SECOND:
-	case IW_MODE_REPEAT:
-		TODO(); /* TODO */
-		break;
-	case IW_MODE_INFRA:
-		/* nothing to be done here... */
-		break;
-	default:
-		dprintk(KERN_ERR PFX "Unknown mode in set_iwmode: %d\n", iw_mode);
-	}
-	if (net_dev->flags & IFF_PROMISC)
-		status |= BCM43xx_SBF_MODE_PROMISC;
 	bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
 
 	value = 0x0002;
-	if (iw_mode != IW_MODE_ADHOC && iw_mode != IW_MODE_MASTER) {
+	if (bcm->interfaces.opmode_adhoc == 0 &&
+	    bcm->interfaces.opmode_ap == 0) {
 		if (bcm->chip_id == 0x4306 && bcm->chip_rev == 3)
 			value = 0x0064;
 		else
@@ -2291,7 +2285,7 @@
 	bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0074, 0x0000);
 
 	/* Initially set the wireless operation mode. */
-	bcm43xx_set_iwmode(bcm, bcm->iw_mode);
+	bcm43xx_select_opmodes(bcm);
 
 	if (bcm->current_core->rev < 3) {
 		bcm43xx_write16(bcm, 0x060E, 0x0000);
@@ -2599,27 +2593,6 @@
 	return err;
 }
 
-static void bcm43xx_gen_bssid(struct bcm43xx_private *bcm)
-{
-	const u8 *mac = (const u8*)(bcm->net_dev->dev_addr);
-	u8 *bssid = bcm->bssid;
-
-	switch (bcm->iw_mode) {
-	case IW_MODE_ADHOC:
-		random_ether_addr(bssid);
-		break;
-	case IW_MODE_MASTER:
-	case IW_MODE_INFRA:
-	case IW_MODE_REPEAT:
-	case IW_MODE_SECOND:
-	case IW_MODE_MONITOR:
-		memcpy(bssid, mac, ETH_ALEN);
-		break;
-	default:
-		assert(0);
-	}
-}
-
 static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm,
 				      u16 rate,
 				      int is_ofdm)
@@ -2746,7 +2719,6 @@
 	/* Maximum Contention Window */
 	bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);
 
-	bcm43xx_gen_bssid(bcm);
 	bcm43xx_write_mac_bssid_templates(bcm);
 
 	if (bcm->current_core->rev >= 5)
@@ -4169,13 +4141,8 @@
 static int bcm43xx_net_open(struct net_device *net_dev)
 {
 	struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
-	int res;
 
-	res = bcm43xx_init_board(bcm);
-	if (!res)
-		return res;
-	bcm43xx_set_iwmode(bcm, bcm->iw_mode);
-	return 0;
+	return bcm43xx_init_board(bcm);
 }
 
 static int bcm43xx_net_stop(struct net_device *net_dev)
@@ -4194,33 +4161,170 @@
 				 struct ieee80211_if_init_conf *conf)
 {
 	struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+	unsigned long flags;
+	int err = -ENOBUFS;
+	struct bcm43xx_interface *iface;
 
-	if (bcm->interfaces > 0)
-		return -ENOBUFS;
-	if (conf->type == IEEE80211_IF_TYPE_MNTR) {
-		bcm->iw_mode = IW_MODE_MONITOR;
-	} else {
-		if (memcmp(bcm->net_dev->dev_addr, conf->mac_addr, ETH_ALEN) != 0)
-			return -EADDRNOTAVAIL;
-		if (conf->type == IEEE80211_IF_TYPE_STA)
-			bcm->iw_mode = IW_MODE_INFRA;
-		else if (conf->type == IEEE80211_IF_TYPE_IBSS)
-			bcm->iw_mode = IW_MODE_ADHOC;
-		else if (conf->type == IEEE80211_IF_TYPE_AP)
-			bcm->iw_mode = IW_MODE_MASTER;
-		else
-			return -EOPNOTSUPP;
+	iface = kzalloc(sizeof(*iface), GFP_KERNEL);
+	if (!iface)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&iface->list);
+	iface->type = conf->type;
+	iface->if_id = conf->if_id;
+	iface->mac_addr = conf->mac_addr;
+
+	bcm43xx_lock_mmio(bcm, flags);
+	switch (conf->type) {
+	case IEEE80211_IF_TYPE_AP:
+		/* Cannot run more than one AP or concurrently
+		 * with IBSS mode.
+		 */
+		if (bcm->interfaces.opmode_ap)
+			break;
+		if (bcm->interfaces.opmode_adhoc)
+			break;
+		bcm->interfaces.opmode_ap++;
+		bcm->interfaces.ap_if = iface;
+		err = 0;
+		break;
+	case IEEE80211_IF_TYPE_STA:
+		/* Cannot run STA and AP or IBSS mode concurrently. */
+		if (bcm->interfaces.opmode_ap)
+			break;
+		if (bcm->interfaces.opmode_adhoc)
+			break;
+		err = 0;
+		break;
+	case IEEE80211_IF_TYPE_IBSS:
+		/* Cannot run more than one IBSS or concurrently
+		 * with AP mode.
+		 */
+		if (bcm->interfaces.opmode_ap)
+			break;
+		if (bcm->interfaces.opmode_adhoc)
+			break;
+		bcm->interfaces.opmode_adhoc++;
+		err = 0;
+		break;
+	case IEEE80211_IF_TYPE_MNTR:
+		bcm->interfaces.opmode_monitor++;
+		err = 0;
+		break;
+	case IEEE80211_IF_TYPE_WDS:
+		//TODO: Uhm..., well.
+		break;
+	default:
+		assert(0);
 	}
-	bcm->interfaces++;
-	return 0;
+	if (!err) {
+		list_add_tail(&iface->list, &bcm->interfaces.list);
+		if (bcm->initialized)
+			bcm43xx_select_opmodes(bcm);
+
+		dprintk(KERN_INFO PFX "Virtual interface added "
+				      "(type: 0x%08X, ID: %d, MAC: "
+				      BCM43xx_MACFMT ")\n",
+			conf->type, conf->if_id,
+			BCM43xx_MACARG(conf->mac_addr));
+	}
+
+	bcm43xx_unlock_mmio(bcm, flags);
+
+	if (err)
+		kfree(iface);
+
+	return err;
 }
 
 static void bcm43xx_remove_interface(struct net_device *net_dev,
 				     struct ieee80211_if_init_conf *conf)
 {
 	struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+	unsigned long flags;
+	struct bcm43xx_interface *iface, *tmp_iface;
+
+	bcm43xx_lock_mmio(bcm, flags);
+	list_for_each_entry_safe(iface, tmp_iface, &bcm->interfaces.list, list) {
+		if (iface->if_id != conf->if_id)
+			continue;
+		assert(conf->type == iface->type);
+
+		switch (iface->type) {
+		case IEEE80211_IF_TYPE_AP:
+			bcm->interfaces.opmode_ap--;
+			assert(bcm->interfaces.opmode_ap == 0);
+			bcm->interfaces.ap_if = NULL;
+			break;
+		case IEEE80211_IF_TYPE_STA:
+			break;
+		case IEEE80211_IF_TYPE_IBSS:
+			bcm->interfaces.opmode_adhoc--;
+			assert(bcm->interfaces.opmode_adhoc == 0);
+			break;
+		case IEEE80211_IF_TYPE_MNTR:
+			assert(bcm->interfaces.opmode_monitor > 0);
+			bcm->interfaces.opmode_monitor--;
+			break;
+		case IEEE80211_IF_TYPE_WDS:
+			//TODO: Uhm..., well.
+			break;
+		default:
+			assert(0);
+		}
+		list_del(&iface->list);
+		kfree(iface);
+		if (bcm->initialized)
+			bcm43xx_select_opmodes(bcm);
+
+		dprintk(KERN_INFO PFX "Virtual interface removed "
+				      "(type: 0x%08X, ID: %d, MAC: "
+				      BCM43xx_MACFMT ")\n",
+			conf->type, conf->if_id,
+			BCM43xx_MACARG(conf->mac_addr));
+		break;
+	}
+	bcm43xx_unlock_mmio(bcm, flags);
+}
+
+static int bcm43xx_config_interface(struct net_device *net_dev,
+				    int if_id,
+				    struct ieee80211_if_conf *conf)
+{
+	struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+	unsigned long flags;
+	struct bcm43xx_interface *iface;
+
+	bcm43xx_lock_mmio(bcm, flags);
+	list_for_each_entry(iface, &bcm->interfaces.list, list) {
+		if (iface->if_id == if_id)
+			goto found;
+	}
+	bcm43xx_unlock_mmio(bcm, flags);
+
+	return -ENODEV;
+found:
+	iface->bssid = conf->bssid;
+	//TODO
+
+	return 0;
+}
+
+static void bcm43xx_set_multicast_list(struct net_device *net_dev,
+				       unsigned short netflags,
+				       int mc_count)
+{
+	struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+	unsigned long flags;
+	u16 old_promisc;
 
-	bcm->interfaces--;
+	bcm43xx_lock_mmio(bcm, flags);
+	old_promisc = bcm->interfaces.opmode_promisc;
+	bcm->interfaces.opmode_promisc = !!(netflags & IFF_PROMISC);
+	if (bcm->interfaces.opmode_promisc != old_promisc) {
+		if (bcm->initialized)
+			bcm43xx_select_opmodes(bcm);
+	}
+	bcm43xx_unlock_mmio(bcm, flags);
 }
 
 /* Initialization of struct net_device, just after allocation. */
@@ -4240,6 +4344,7 @@
 	int err;
 
 	bcm->ieee = ieee;
+	INIT_LIST_HEAD(&bcm->interfaces.list);
 	bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;
 	bcm->pci_dev = pci_dev;
 	bcm->net_dev = net_dev;
@@ -4294,6 +4399,8 @@
 	ieee->name = KBUILD_MODNAME;
 	ieee->host_gen_beacon = 1;
 	ieee->rx_includes_fcs = 1;
+	ieee->monitor_during_oper = 1;
+	ieee->channel_change_time = 20000;
 	ieee->tx = bcm43xx_net_hard_start_xmit;
 	ieee->open = bcm43xx_net_open;
 	ieee->stop = bcm43xx_net_stop;
@@ -4301,6 +4408,8 @@
 	ieee->remove_interface = bcm43xx_remove_interface;
 	ieee->reset = bcm43xx_net_reset;
 	ieee->config = bcm43xx_net_config;
+	ieee->config_interface = bcm43xx_config_interface;
+	ieee->set_multicast_list = bcm43xx_set_multicast_list;
 //TODO	ieee->set_key = bcm43xx_net_set_key;
 	ieee->get_stats = bcm43xx_net_get_stats;
 	ieee->queues = 1;
Index: wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c
===================================================================
--- wireless-dev.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c	2006-04-28 16:13:40.000000000 +0200
+++ wireless-dev/drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c	2006-05-01 18:56:17.000000000 +0200
@@ -94,7 +94,7 @@
 		bcm43xx_mac_suspend(bcm);
 		spin_lock(&phy->lock);
 	} else {
-		if (bcm->iw_mode != IW_MODE_MASTER)
+		if (bcm->interfaces.opmode_ap == 0)
 			bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
 	}
 	phy->is_locked = 1;
@@ -111,7 +111,7 @@
 			bcm43xx_mac_enable(bcm);
 		}
 	} else {
-		if (bcm->iw_mode != IW_MODE_MASTER)
+		if (bcm->interfaces.opmode_ap == 0)
 			bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
 	}
 	phy->is_locked = 0;


-- 
Greetings Michael.

[-- Attachment #2: Type: application/pgp-signature, Size: 191 bytes --]

             reply	other threads:[~2006-05-01 19:33 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-05-01 19:35 Michael Buesch [this message]
2006-05-02 11:20 ` [PATCH] bcm43xx-d80211: proper implementation of virtual interface support Jiri Benc
2006-05-04 19:26   ` Marcus Better
2006-05-04 20:55     ` Ivo van Doorn
2006-05-04 22:22       ` Jiri Benc
2006-05-05  5:45         ` Ulrich Kunitz
2006-05-05 10:15           ` Johannes Berg
2006-05-05 10:34             ` Jiri Benc

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200605012135.00895.mb@bu3sch.de \
    --to=mb@bu3sch.de \
    --cc=bcm43xx-dev@lists.berlios.de \
    --cc=jbenc@suse.cz \
    --cc=linville@tuxdriver.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.