summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 8ffe11358..672ff9c2d 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 a1bddd5db..d924941f2 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 7d10aaa28..32547b5f3 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;