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; @@ -926,6 +926,65 @@ static void net80211_add_channels ( struct net80211_device *dev, int start, dev->nr_channels = i; } +/** + * 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 * @@ -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;