summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Oreman2009-08-07 10:38:23 +0200
committerMichael Brown2009-08-09 01:11:26 +0200
commitf128a6db21339964c52fb7bcfc6c8a3ffa96387b (patch)
tree72f88f3acbcf7219685f16e60e3b1729c636d1c7
parent[geniso] Emit proper error message for incorrect location of ISOLINUX_BIN (diff)
downloadipxe-f128a6db21339964c52fb7bcfc6c8a3ffa96387b.tar.gz
ipxe-f128a6db21339964c52fb7bcfc6c8a3ffa96387b.tar.xz
ipxe-f128a6db21339964c52fb7bcfc6c8a3ffa96387b.zip
[802.11] Enhance support for driver PHY differences
The prior net80211 model of physical-layer behavior for drivers was overly simplistic and limited the drivers that could be written. To be more flexible, split the driver-provided list of supported rates by band, and add a means for specifying a list of supported channels. Allow drivers to specify a hardware channel value that will be tied to uses of the channel. Expose net80211_duration() to drivers, and make the rate it uses in its computations configurable, so that it can be used in calculating durations that must be set in hardware for ACK and CTS packets. Add net80211_cts_duration() for the common case of calculating the duration for a CTS packet. Signed-off-by: Michael Brown <mcb30@etherboot.org>
-rw-r--r--src/drivers/net/rtl818x/rtl818x.c56
-rw-r--r--src/include/gpxe/net80211.h45
-rw-r--r--src/net/80211/net80211.c90
3 files changed, 147 insertions, 44 deletions
diff --git a/src/drivers/net/rtl818x/rtl818x.c b/src/drivers/net/rtl818x/rtl818x.c
index 8ffe1135..672ff9c2 100644
--- a/src/drivers/net/rtl818x/rtl818x.c
+++ b/src/drivers/net/rtl818x/rtl818x.c
@@ -186,13 +186,16 @@ static int rtl818x_tx(struct net80211_device *dev, struct io_buffer *iob)
plcp_len |= 1 << 15;
}
+ entry = &priv->tx_ring[priv->tx_prod];
+
if (dev->phy_flags & NET80211_PHY_USE_PROTECTION) {
tx_flags |= RTL818X_TX_DESC_FLAG_CTS;
tx_flags |= priv->hw_rtscts_rate << 19;
+ entry->rts_duration = net80211_cts_duration(dev, len);
+ } else {
+ entry->rts_duration = 0;
}
- entry = &priv->tx_ring[priv->tx_prod];
-
if (entry->flags & RTL818X_TX_DESC_FLAG_OWN) {
/* card hasn't processed the old packet yet! */
return -EBUSY;
@@ -201,7 +204,6 @@ static int rtl818x_tx(struct net80211_device *dev, struct io_buffer *iob)
priv->tx_buf[priv->tx_prod] = iob;
priv->tx_prod = (priv->tx_prod + 1) % RTL818X_TX_RING_SIZE;
- entry->rts_duration = 0;
entry->plcp_len = cpu_to_le16(plcp_len);
entry->tx_buf = cpu_to_le32(virt_to_bus(iob->data));
entry->frame_len = cpu_to_le32(len);
@@ -626,20 +628,7 @@ static struct bit_basher_operations rtl818x_basher_ops = {
.write = rtl818x_spi_write_bit,
};
-/* The net80211 code makes a copy of this, so we're OK modifying the
- static version as we initialize the card, as long as we don't
- depend on possibly-modified values in case there are multiple cards. */
-static struct net80211_hw_info rtl818x_hwinfo = {
- /* MAC address filled in at runtime */
- /* modes filled in at runtime */
- .bands = NET80211_BAND_2GHZ,
- .flags = NET80211_HW_RX_HAS_FCS,
- .signal_type = NET80211_SIGNAL_ARBITRARY,
- /* supported rates filled in at runtime */
- .signal_max = 65,
- .channel_change_time = 1000, /* no idea what the actual value is */
-};
-
+#if DBGLVL_MAX
static const char *rtl818x_rf_names[] = {
NULL, /* no 0 */
"Intersil", "RFMD", /* unsupported 1-2 */
@@ -649,6 +638,7 @@ static const char *rtl818x_rf_names[] = {
"RTL8255", /* unsupported 10 */
};
#define RTL818X_NR_RF_NAMES 11
+#endif
struct net80211_device_operations rtl818x_operations = {
.open = rtl818x_start,
@@ -669,6 +659,13 @@ static int rtl818x_probe(struct pci_device *pdev,
const char *chip_name;
u32 reg;
u16 eeprom_val;
+ struct net80211_hw_info *hwinfo;
+
+ hwinfo = zalloc(sizeof(*hwinfo));
+ if (!hwinfo) {
+ DBG("rtl818x: hwinfo alloc failed\n");
+ return -ENOMEM;
+ }
adjust_pci_device(pdev);
@@ -715,15 +712,21 @@ static int rtl818x_probe(struct pci_device *pdev,
priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC;
- memcpy(rtl818x_hwinfo.supported_rates, rtl818x_rates,
+ hwinfo->bands = NET80211_BAND_BIT_2GHZ;
+ hwinfo->flags = NET80211_HW_RX_HAS_FCS;
+ hwinfo->signal_type = NET80211_SIGNAL_ARBITRARY;
+ hwinfo->signal_max = 65;
+ hwinfo->channel_change_time = 1000;
+
+ memcpy(hwinfo->rates[NET80211_BAND_2GHZ], rtl818x_rates,
sizeof(*rtl818x_rates) * RTL818X_NR_RATES);
if (priv->r8185) {
- rtl818x_hwinfo.modes = NET80211_MODE_B | NET80211_MODE_G;
- rtl818x_hwinfo.nr_supported_rates = RTL818X_NR_RATES;
+ hwinfo->modes = NET80211_MODE_B | NET80211_MODE_G;
+ hwinfo->nr_rates[NET80211_BAND_2GHZ] = RTL818X_NR_RATES;
} else {
- rtl818x_hwinfo.modes = NET80211_MODE_B;
- rtl818x_hwinfo.nr_supported_rates = RTL818X_NR_B_RATES;
+ hwinfo->modes = NET80211_MODE_B;
+ hwinfo->nr_rates[NET80211_BAND_2GHZ] = RTL818X_NR_B_RATES;
}
priv->spibit.basher.op = &rtl818x_basher_ops;
@@ -755,6 +758,7 @@ static int rtl818x_probe(struct pci_device *pdev,
}
if (!priv->rf) {
+#if DBGLVL_MAX
if (eeprom_val < RTL818X_NR_RF_NAMES &&
rtl818x_rf_names[eeprom_val] != NULL)
DBG("rtl818x: %s RF frontend not supported!\n",
@@ -762,6 +766,7 @@ static int rtl818x_probe(struct pci_device *pdev,
else
DBG("rtl818x: RF frontend #%d not recognized!\n",
eeprom_val);
+#endif
err = -ENOSYS;
goto err_free_dev;
@@ -777,7 +782,7 @@ static int rtl818x_probe(struct pci_device *pdev,
}
/* read the MAC address */
- nvs_read(&priv->eeprom.nvs, 0x7, rtl818x_hwinfo.hwaddr, 6);
+ nvs_read(&priv->eeprom.nvs, 0x7, hwinfo->hwaddr, 6);
/* CCK TX power */
for (i = 0; i < 14; i += 2) {
@@ -799,12 +804,14 @@ static int rtl818x_probe(struct pci_device *pdev,
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
- err = net80211_register(dev, &rtl818x_operations, &rtl818x_hwinfo);
+ err = net80211_register(dev, &rtl818x_operations, hwinfo);
if (err) {
DBG("rtl818x: cannot register device\n");
goto err_free_dev;
}
+ free(hwinfo);
+
DBG("rtl818x: Realtek RTL818%s (RF chip %s) with address %s\n",
chip_name, priv->rf->name, netdev_hwaddr(dev->netdev));
@@ -813,6 +820,7 @@ static int rtl818x_probe(struct pci_device *pdev,
err_free_dev:
pci_set_drvdata(pdev, NULL);
net80211_free(dev);
+ free(hwinfo);
return err;
}
diff --git a/src/include/gpxe/net80211.h b/src/include/gpxe/net80211.h
index a1bddd5d..d924941f 100644
--- a/src/include/gpxe/net80211.h
+++ b/src/include/gpxe/net80211.h
@@ -42,9 +42,16 @@ FILE_LICENCE ( GPL2_OR_LATER );
/** @{ */
/** The 2.4 GHz ISM band, unlicensed in most countries */
-#define NET80211_BAND_2GHZ (1 << 0)
+#define NET80211_BAND_2GHZ 0
/** The band from 4.9 GHz to 5.7 GHz, which tends to be more restricted */
-#define NET80211_BAND_5GHZ (1 << 1)
+#define NET80211_BAND_5GHZ 1
+/** Number of RF bands */
+#define NET80211_NR_BANDS 2
+
+/** Bitmask for the 2GHz band */
+#define NET80211_BAND_BIT_2GHZ (1 << 0)
+/** Bitmask for the 5GHz band */
+#define NET80211_BAND_BIT_5GHZ (1 << 1)
/** @} */
@@ -397,6 +404,9 @@ struct net80211_channel
*/
u16 center_freq;
+ /** Hardware channel value */
+ u16 hw_value;
+
/** Maximum allowable transmit power, in dBm
*
* This should be interpreted as EIRP, the power supplied to
@@ -478,15 +488,21 @@ struct net80211_hw_info
*/
unsigned signal_max;
- /** List of transmission rates supported by the card
+ /** List of RF channels supported by the card */
+ struct net80211_channel channels[NET80211_MAX_CHANNELS];
+
+ /** Number of supported channels */
+ int nr_channels;
+
+ /** List of transmission rates supported by the card, indexed by band
*
* Rates should be in 100kbps increments (e.g. 11 Mbps would
* be represented as the number 110).
*/
- u16 supported_rates[NET80211_MAX_RATES];
+ u16 rates[NET80211_NR_BANDS][NET80211_MAX_RATES];
- /** Number of supported rates */
- int nr_supported_rates;
+ /** Number of supported rates, indexed by band */
+ int nr_rates[NET80211_NR_BANDS];
/** Estimate of the time required to change channels, in microseconds
*
@@ -969,6 +985,7 @@ struct net80211_device *net80211_alloc ( size_t priv_size );
int net80211_register ( struct net80211_device *dev,
struct net80211_device_operations *ops,
struct net80211_hw_info *hw );
+u16 net80211_duration ( struct net80211_device *dev, int bytes, u16 rate );
void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
int signal, u16 rate );
void net80211_rx_err ( struct net80211_device *dev,
@@ -979,5 +996,21 @@ void net80211_unregister ( struct net80211_device *dev );
void net80211_free ( struct net80211_device *dev );
/** @} */
+/**
+ * Calculate duration field for a CTS control frame
+ *
+ * @v dev 802.11 device
+ * @v size Size of the packet being cleared to send
+ *
+ * A CTS control frame's duration field captures the frame being
+ * protected and its 10-byte ACK.
+ */
+static inline u16 net80211_cts_duration ( struct net80211_device *dev,
+ int size )
+{
+ return ( net80211_duration ( dev, 10,
+ dev->rates[dev->rtscts_rate] ) +
+ net80211_duration ( dev, size, dev->rates[dev->rate] ) );
+}
#endif
diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c
index 7d10aaa2..32547b5f 100644
--- a/src/net/80211/net80211.c
+++ b/src/net/80211/net80211.c
@@ -154,7 +154,6 @@ static void net80211_netdev_irq ( struct net_device *netdev, int enable );
* @defgroup net80211_linklayer 802.11 link-layer protocol functions
* @{
*/
-static u16 net80211_duration ( struct net80211_device *dev, int bytes );
static int net80211_ll_push ( struct net_device *netdev,
struct io_buffer *iobuf, const void *ll_dest,
const void *ll_source, uint16_t net_proto );
@@ -171,6 +170,7 @@ static int net80211_ll_mc_hash ( unsigned int af, const void *net_addr,
*/
static void net80211_add_channels ( struct net80211_device *dev, int start,
int len, int txpower );
+static void net80211_filter_hw_channels ( struct net80211_device *dev );
static void net80211_set_rtscts_rate ( struct net80211_device *dev );
static int net80211_process_capab ( struct net80211_device *dev,
u16 capab );
@@ -430,10 +430,9 @@ static inline int net80211_rate_is_erp ( u16 rate )
*
* No other frame types are currently supported by gPXE.
*/
-static u16 net80211_duration ( struct net80211_device *dev, int bytes )
+u16 net80211_duration ( struct net80211_device *dev, int bytes, u16 rate )
{
struct net80211_channel *chan = &dev->channels[dev->channel];
- u16 rate = dev->rates[dev->rate];
u32 kbps = rate * 100;
if ( chan->band == NET80211_BAND_5GHZ || net80211_rate_is_erp ( rate ) ) {
@@ -496,7 +495,7 @@ static int net80211_ll_push ( struct net_device *netdev,
/* We don't send fragmented frames, so duration is the time
for an SIFS + 10-byte ACK. */
- hdr->duration = net80211_duration ( dev, 10 );
+ hdr->duration = net80211_duration ( dev, 10, dev->rates[dev->rate] );
memcpy ( hdr->addr1, dev->bssid, ETH_ALEN );
memcpy ( hdr->addr2, ll_source, ETH_ALEN );
@@ -712,7 +711,7 @@ int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 dest[6],
hdr->fc = IEEE80211_THIS_VERSION | IEEE80211_TYPE_MGMT |
( fc & ~IEEE80211_FC_PROTECTED );
- hdr->duration = net80211_duration ( dev, 10 );
+ hdr->duration = net80211_duration ( dev, 10, dev->rates[dev->rate] );
hdr->seq = IEEE80211_MAKESEQ ( ++dev->last_tx_seqnr, 0 );
memcpy ( hdr->addr1, dest, ETH_ALEN ); /* DA = RA */
@@ -908,6 +907,7 @@ static void net80211_add_channels ( struct net80211_device *dev, int start,
for ( i = dev->nr_channels; len-- && i < NET80211_MAX_CHANNELS; i++ ) {
dev->channels[i].channel_nr = chan;
dev->channels[i].maxpower = txpower;
+ dev->channels[i].hw_value = 0;
if ( chan >= 1 && chan <= 14 ) {
dev->channels[i].band = NET80211_BAND_2GHZ;
@@ -927,6 +927,65 @@ static void net80211_add_channels ( struct net80211_device *dev, int start,
}
/**
+ * Filter 802.11 device channels for hardware capabilities
+ *
+ * @v dev 802.11 device
+ *
+ * Hardware may support fewer channels than regulatory restrictions
+ * allow; this function filters out channels in dev->channels that are
+ * not supported by the hardware list in dev->hwinfo. It also copies
+ * over the net80211_channel::hw_value and limits maximum TX power
+ * appropriately.
+ *
+ * Channels are matched based on center frequency, ignoring band and
+ * channel number.
+ *
+ * If the driver specifies no supported channels, the effect will be
+ * as though all were supported.
+ */
+static void net80211_filter_hw_channels ( struct net80211_device *dev )
+{
+ int delta = 0, i = 0;
+ int old_freq = dev->channels[dev->channel].center_freq;
+ struct net80211_channel *chan, *hwchan;
+
+ if ( ! dev->hw->nr_channels )
+ return;
+
+ dev->channel = 0;
+ for ( chan = dev->channels; chan < dev->channels + dev->nr_channels;
+ chan++, i++ ) {
+ int ok = 0;
+ for ( hwchan = dev->hw->channels;
+ hwchan < dev->hw->channels + dev->hw->nr_channels;
+ hwchan++ ) {
+ if ( hwchan->center_freq == chan->center_freq ) {
+ ok = 1;
+ break;
+ }
+ }
+
+ if ( ! ok )
+ delta++;
+ else {
+ chan->hw_value = hwchan->hw_value;
+ if ( hwchan->maxpower != 0 &&
+ chan->maxpower > hwchan->maxpower )
+ chan->maxpower = hwchan->maxpower;
+ if ( old_freq == chan->center_freq )
+ dev->channel = i - delta;
+ if ( delta )
+ chan[-delta] = *chan;
+ }
+ }
+
+ dev->nr_channels -= delta;
+
+ if ( dev->channels[dev->channel].center_freq != old_freq )
+ dev->op->config ( dev, NET80211_CFG_CHANNEL );
+}
+
+/**
* Update 802.11 device state to reflect received capabilities field
*
* @v dev 802.11 device
@@ -981,6 +1040,7 @@ static int net80211_process_ie ( struct net80211_device *dev,
int have_rates = 0, i;
int ds_channel = 0;
int changed = 0;
+ int band = dev->channels[dev->channel].band;
if ( ( void * ) ie >= ie_end )
return 0;
@@ -1042,6 +1102,7 @@ static int net80211_process_ie ( struct net80211_device *dev,
t->band.max_txpower );
}
}
+ net80211_filter_hw_channels ( dev );
break;
case IEEE80211_IE_ERP_INFO:
@@ -1067,9 +1128,8 @@ static int net80211_process_ie ( struct net80211_device *dev,
dev->rate = 0;
for ( i = 0; i < dev->nr_rates; i++ ) {
int ok = 0;
- for ( j = 0; j < dev->hw->nr_supported_rates; j++ ) {
- if ( dev->hw->supported_rates[j] ==
- dev->rates[i] ) {
+ for ( j = 0; j < dev->hw->nr_rates[band]; j++ ) {
+ if ( dev->hw->rates[band][j] == dev->rates[i] ){
ok = 1;
break;
}
@@ -1597,7 +1657,7 @@ static void net80211_step_associate ( struct process *proc )
int band = dev->hw->bands;
if ( active )
- band &= ~NET80211_BAND_5GHZ;
+ band &= ~NET80211_BAND_BIT_5GHZ;
rc = net80211_prepare_probe ( dev, band, active );
if ( rc )
@@ -1916,7 +1976,7 @@ int net80211_prepare_probe ( struct net80211_device *dev, int band,
{
assert ( dev->netdev->state & NETDEV_OPEN );
- if ( active && band != NET80211_BAND_2GHZ ) {
+ if ( active && ( band & NET80211_BAND_BIT_5GHZ ) ) {
DBGC ( dev, "802.11 %p cannot perform active scanning on "
"5GHz band\n", dev );
return -EINVAL_ACTIVE_SCAN;
@@ -1935,22 +1995,24 @@ int net80211_prepare_probe ( struct net80211_device *dev, int band,
if ( active )
net80211_add_channels ( dev, 1, 11, NET80211_REG_TXPOWER );
else {
- if ( band & NET80211_BAND_2GHZ )
+ if ( band & NET80211_BAND_BIT_2GHZ )
net80211_add_channels ( dev, 1, 14,
NET80211_REG_TXPOWER );
- if ( band & NET80211_BAND_5GHZ )
+ if ( band & NET80211_BAND_BIT_5GHZ )
net80211_add_channels ( dev, 36, 8,
NET80211_REG_TXPOWER );
}
+ net80211_filter_hw_channels ( dev );
+
/* Use channel 1 for now */
dev->channel = 0;
dev->op->config ( dev, NET80211_CFG_CHANNEL );
- /* Always do active probes at 1Mbps */
+ /* Always do active probes at lowest (presumably first) speed */
dev->rate = 0;
dev->nr_rates = 1;
- dev->rates[0] = 10;
+ dev->rates[0] = dev->hw->rates[dev->channels[0].band][0];
dev->op->config ( dev, NET80211_CFG_RATE );
return 0;