[802.11] Enhance support for driver PHY differences
authorJoshua Oreman <oremanj@rwcr.net>
Fri, 7 Aug 2009 08:38:23 +0000 (01:38 -0700)
committerMichael Brown <mcb30@etherboot.org>
Sat, 8 Aug 2009 23:11:26 +0000 (00:11 +0100)
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>
src/drivers/net/rtl818x/rtl818x.c
src/include/gpxe/net80211.h
src/net/80211/net80211.c

index 8ffe113..672ff9c 100644 (file)
@@ -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;
 }
 
index a1bddd5..d924941 100644 (file)
@@ -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
index 7d10aaa..32547b5 100644 (file)
@@ -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;