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 --]
next 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).