When the 802.11 interface connects to another station, a directory is created in debugfs with the station's MAC address. Note that if the interface is in client mode, it will only connect to one station—the AP—while in other modes it may connect to multiple stations. To list these stations:
$ sudo ls /sys/kernel/debug/ieee80211/phy0/netdev:wlan0/stations/ 00:0f:34:9d:01:a0
(The names phy0 and wlan0 used here may be different on your system, particularly if you have more than one wireless adapter.) The directory for each station will contain several entries with different information about the station. One of these entries is rate_scale_table, shown here:
$ sudo cat /sys/kernel/debug/ieee80211/phy0/netdev:wlan0/stations/00:0f:34:9d:01:a0/rate_scale_table sta_id 0 failed=0 success=0 rate=0FFF fixed rate 0x0 valid_tx_ant ANT_A,ANT_B,ANT_C lq type legacy last tx rate=0x420A general:flags=0x0 mimo-d=0 s-ant0x1 d-ant=0x3 agg:time_limit=4000 dist_start_th=3 frame_cnt_limit=31 Start idx [0]=0x0 [1]=0x0 [2]=0x0 [3]=0x0 rate[0] 0x420A 1mbps rate[1] 0x420A 1mbps rate[2] 0x420A 1mbps rate[3] 0x420A 1mbps rate[4] 0x420A 1mbps rate[5] 0x420A 1mbps rate[6] 0x420A 1mbps rate[7] 0x420A 1mbps rate[8] 0x420A 1mbps rate[9] 0x420A 1mbps rate[10] 0x420A 1mbps rate[11] 0x420A 1mbps rate[12] 0x420A 1mbps rate[13] 0x420A 1mbps rate[14] 0x420A 1mbps rate[15] 0x420A 1mbps
This output indicates that for the station with the MAC address 00:0f:34:9d:01:a0,
The rates shown above are 32-bit values in the rate_n_flags format described in the source code. Breaking down the value 0x420A, for example, reveals that:
0x420A = 0x4000 (RATE_MCS_ANT_A_MSK) | 0x0200 (RATE_MCS_CCK_MSK) | 0x000A (10 = 1 Mbps)
As a different example, the value for a High Throughput station might be 0x1c113, where:
0x1c113 = 0x1c000 (RATE_MCS_ANT_ABC_MSK) | 0x00000 (! RATE_MCS_SGI_MSK) | 0x00000 (! RATE_MCS_HT40_MSK) | 0x00100 (RATE_MCS_HT_MSK) | 0x00013 (19)
These parameters correspond to a data rate of 78 Mbps; refer to Tables 20-29 through 20-43 in the IEEE 802.11n standard (summarized here). Note: the adapter only supports MCS 0–23.
The driver allows you to override its rate selection algorithm for a station, setting all of the 16 possible transmit rates to the same value, by writing the value to rate_scale_table for the station:
$ echo 0x4214 | sudo tee \ > /sys/kernel/debug/ieee80211/phy0/netdev:wlan0/stations/00:0f:34:9d:01:a0/rate_scale_table
This example sets the transmit rate for the station above to 2 Mbps, using Antenna A. (Use care: setting an invalid rate may cause the firmware to crash or the driver to stop functioning.)
Another entry in debugfs allows setting the transmit rate for broadcast frames. Note that this does not have any effect in client mode, since broadcast frames are transmitted to the AP first.
$ echo 0x1c113 | sudo tee /sys/kernel/debug/ieee80211/phy0/iwlwifi/iwldvm/debug/bcast_tx_rate
(For kernel versions prior to 3.6, remove the iwldvm/ component from the path.)
A separate entry in debugfs can be written to select the transmit rate that will be used when injecting frames in monitor mode:
$ echo 0x1c113 | sudo tee /sys/kernel/debug/ieee80211/phy0/iwlwifi/iwldvm/debug/monitor_tx_rate
(For kernel versions prior to 3.6, remove the iwldvm/ component from the path.)
Using MATLAB/Octave, change to the matlab directory in the CSI Tool supplementary material:
cd linux-80211n-csitool-supplementary/matlab
Now read in the CSI trace file. A sample file is included in the supplementary material, but you can also use the file that is generated by following the last step of the installation instructions.
csi_trace = read_bf_file('sample_data/log.all_csi.6.7.6');
Note that this uses a MEX-file compiled from read_bfee.c to unpack the binary CSI format. If this does not work, recompile this MEX-file using MATLAB/Octave and try again.
In our sample file, csi_trace is a 1x29 cell array, which holds 29 structs. This contains the CSI information for 29 received packets. Let's inspect one of the entries:
>> csi_entry = csi_trace{1} (Note the curly-braces {}, not parentheses ().) csi_entry = timestamp_low: 4 (In the sample trace, timestamp_low is invalid and always 4.) bfee_count: 72 Nrx: 3 Ntx: 1 rssi_a: 33 rssi_b: 37 rssi_c: 41 noise: -127 agc: 38 perm: [3 2 1] rate: 256 csi: [1x3x30 double]
Let's break down this display:
4
.Now that we've described all the fields of this struct, we need to put them all together to compute the CSI in absolute units, rather than Intel's internal reference level. In particular, we need to combine the RSSI and AGC values together to get RSS in dBm, and include noise to get SNR. If there is no noise, as in the sample case, we instead use a hard-coded noise floor of -92 dBm. We use the script get_scaled_csi.m to do this:
>> csi = get_scaled_csi(csi_entry);
Finally, csi is a 1×3×30 matrix that represents the MIMO channel state for this link. It's units are in linear—i.e., not dB—voltage space. This is the format used in all textbooks I've seen, that is, we've normalized the CSI (in textbooks, usually called H) such that there is unit noise.
Let's look at the three different spatial paths on the 1×3 link we measured:
>> plot(db(abs(squeeze(csi).'))) >> legend('RX Antenna A', 'RX Antenna B', 'RX Antenna C', 'Location', 'SouthEast' ); >> xlabel('Subcarrier index'); >> ylabel('SNR [dB]');
In the plot command, squeeze() turns csi into a 3×30 matrix by removing the first singleton dimension. db() converts from linear (voltage) space into logarithmic (base-10, power) space. abs converts each complex number into its magnitude. Finally, the .' operator transposes the squeezed CSI from 3×30 matrix into a 30×3 matrix, and does not complement the complex numbers. Combined, we get the plot below.
We see that this is a mostly flat link, with relatively little frequency-selective fading (around 3 dB for most antenna pairs). However, there is a fair (perhaps 8 dB) difference between the best antenna C and the worst antenna A. This matches the difference between rssi_a and rssi_c (as we expect it should).
We'll conclude our discussion of the CSI by showing you how to compute the Effective SNR from our CSI matrices. To do so, we use the get_eff_SNRs script, which takes as input a CSI matrix and returns a 7×4 matrix of effective SNR values in linear (power) space. The 4 columns correspond to the effective SNR using the four 802.11 modulation schemes, namely BPSK/QPSK/16QAM/64QAM. The 7 rows correspond to the seven possible antenna selections with 3 antennas and 1, 2, or 3 spatial streams. In particular, the first 3 rows correspond to single-stream transmissions with antenna A, B, or C. The next 3 rows correspond to dual-stream transmissions with antennas AB, AC, or BC. The last row corresponds to a 3-stream transmission using all antennas.
>> db(get_eff_SNRs(csi), 'pow') ans = 22.1821 22.2698 22.9007 24.6297 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356 -156.5356
Okay, that's pretty disappointing! What happened? Well, note that this is a 1×3 link, so the only valid antenna configuration is SIMO with the single transmit antenna we measured. The other 6 rows correspond to a very small SNR, i.e, a large, negative dB.
Let's look at a 3×3 matrix instead:
>> csi_entry = csi_trace{20} csi_entry = timestamp_low: 4 bfee_count: 91 Nrx: 3 Ntx: 3 rssi_a: 34 rssi_b: 39 rssi_c: 39 noise: -127 agc: 40 perm: [2 3 1] rate: 272 csi: [3x3x30 double] >> csi = get_scaled_csi(csi_entry); >> db(get_eff_SNRs(csi), 'pow') ans = Inf Inf 32.3435 32.6069 Inf Inf 32.4238 32.6822 Inf Inf 32.2353 32.5051 25.4763 25.5262 25.8974 26.8482 24.6893 24.7490 25.1933 26.5660 21.9185 22.0303 22.8060 24.6483 6.5818 8.2321 12.4185 16.2016
Here, all 7 rows are valid because there are three transmit antennas. We see that all the SIMO streams are very likely to work; in fact, for BPSK and QPSK there are so few errors that MATLAB's error functions can't distinguish it from zero, and the SNR is effectively infinite. The MIMO2 rates are also likely to work, but only some of the MIMO3 rates will work. See our SIGCOMM 2010 paper for more details.
If NetworkManager is installed, did you disable its control of the wireless interface? This is needed if you are using command line utilities such as iw to configure the interface. Using Ubuntu:
echo iface wlan0 inet manual | sudo tee -a /etc/network/interfaces sudo restart network-manager
No. The tool relies on my custom firmware image which only works on the IWL 5300.
CSI is measured from a received packet under the following conditions:
The packet is transmitted using an 802.11n "HT" rate.
The receiver's logical configuration (e.g., channel type is HT, and channel width) and its physical configuration (e.g., correct number of antennas connected, all antennas work in the configured band, all antennas are enabled in the firmware) support the transmitted rate.
The receiver decodes the packet correctly, with no errors.
AND depending on the mode:
Client mode: The packet is transmitted by the connected AP, and the destination is either the local address or broadcast.
AP mode: The packet is transmitted by a connected client.
IBSS (ad-hoc) mode: The packet is transmitted by another station in the connected IBSS, and the destination is either the local address or broadcast.
Monitor mode: The packet is addressed both to and from a hardcoded, fixed address 00:16:ea:12:34:56. See the injection scripts, using mode 1
only.
In other words, CSI is not recorded for overheard 802.11n conversations. It is also not recorded for beacons from a connected AP if they are sent a non-HT rate (e.g., the 802.11a/g 6 Mbps rate). I also note that there is only one CSI measurement per 802.11n A-MPDU (aggregation batch) since there is really only "one" preamble.
The set of subcarriers measured is specified by the IEEE 802.11n-2009 standard (in Table 7-25f on page 50). Intel's implementation only supports the versions that measure 30 subcarriers each, corresponding to Ng = 2 for 20 MHz and Ng = 4 for 40 MHz.
The firmware for this device did not have enough code room for both the beamforming software paths (required to measure CSI) and the encryption software paths (required for WEP/WPA/WPA2/etc. networks).
There may be a workaround if you can get the kernel software stack to use the kernel's software implementation of crypto, but I've never had the time to try it. Please let me know if you get it to work!
Our changes to kernel code don't affect the existing licenses in the kernel: GPLv2.
The firmware is under the same license as Intel's firmware. You can find it here.
Any code that we wrote is under the MIT license.
No.
We produced the modified firmware while employed at Intel with access to proprietary code, documents, and tools. We no longer have access to these tools and, unless you work at Intel, neither do you. Sorry!
A very strong thank you to Intel for very graciously allowing us to use and distribute the binary version of the modified firmware!
The access point implementation on this chipset is fairly incomplete; the feature was started and abandoned internally, and the engineers never subjected it to rigorous tests. We enabled it for the CSI Tool as a hack. In our experiments, AP mode seemed to work well enough for links that also used an Intel 5300 client in a quiet network, but other Wi-Fi devices sometimes cause unavoidable crashes.
In our own work, we found the CSI Tool to be most effective when using the injection scripts with devices in monitor mode. Client mode, AP mode, and ad-hoc modes never worked well, if at all, for us.
First, please read #5 – "For which packets is CSI measured?". Hopefully, this description will make it clear why you are unlikely to ever get many good measurements in traditional AP/client networks.
We recommend that you use injection mode for most multi-link measurements. Please see the instructions here.
First, please read #5 – "For which packets is CSI measured?". This answer provides useful background for understanding the relationship and interleaving between CSI measurements and other logs.
You can find the information that may be logged by looking at the iwl-connector.h
header file in the driver folder. Each of these flags may be set to relay one or more types of messages from the kernel to userspace programs like log_to_file
.
The default setting is IWL_CONN_BFEE_NOTIF_MSK
-- the beamforming information feedback is logged. Other options:
IWL_CONN_RX_PHY_MSK
will log additional physical information. However, we already copy the most relevant information from this structure into the beamforming response (see code here).IWL_CONN_RX_MPDU_MSK
will log packet payloads. This message should include, e.g., the IEEE 802.11 sequence number and of course packet payload which you could use for application or measurement-specific purposes.To set which information is logged, please set the mask to the union of all information you require to be logged. For example, to log both CSI measurements and packet payloads, the mask would be IWL_CONN_BFEE_NOTIF_MSK | IWL_CONN_RX_MPDU_MSK
, or 0x5
. Once you know the desired mask, you would set it by (re-)loading the iwlwifi
module with the connector_log
flag set:
modprobe iwlwifi connector_log=0x5
The default value for connector_log
is 0
, and the recommended value in the installation instructions is 0x1
, which only logs beamforming information.
These modes require beaconing, which cannot be performed by this adapter on channels where radar detection is required (since it is not implemented). The regulations for specific channels, including whether radar detection is required, vary by country.
First, make sure that the regulatory domain is set in the kernel, so that it is not world roaming.
The adapter's EEPROM also contains regulatory settings for each channel, which might be more or less restrictive than the actual regulations where you are operating. The firmware reads and uses the regulatory settings directly from the EEPROM. The driver reads the settings from the EEPROM and combines them with the regulatory data used by the kernel; to examine the result:
$ iw phy Wiphy phy0 Band 1: [ ... ] Frequencies: * 2412 MHz [1] (24.0 dBm) * 2417 MHz [2] (24.0 dBm) * 2422 MHz [3] (24.0 dBm) * 2427 MHz [4] (24.0 dBm) * 2432 MHz [5] (24.0 dBm) * 2437 MHz [6] (24.0 dBm) * 2442 MHz [7] (24.0 dBm) * 2447 MHz [8] (24.0 dBm) * 2452 MHz [9] (24.0 dBm) * 2457 MHz [10] (24.0 dBm) * 2462 MHz [11] (24.0 dBm) [ ... ] Band 2: [ ... ] Frequencies: * 5180 MHz [36] (14.0 dBm) * 5200 MHz [40] (14.0 dBm) * 5220 MHz [44] (14.0 dBm) * 5240 MHz [48] (14.0 dBm) * 5260 MHz [52] (17.0 dBm) (radar detection) * 5280 MHz [56] (17.0 dBm) (radar detection) * 5300 MHz [60] (17.0 dBm) (radar detection) * 5320 MHz [64] (17.0 dBm) (radar detection) * 5500 MHz [100] (17.0 dBm) (radar detection) * 5520 MHz [104] (17.0 dBm) (radar detection) * 5540 MHz [108] (17.0 dBm) (radar detection) * 5560 MHz [112] (17.0 dBm) (radar detection) * 5580 MHz [116] (17.0 dBm) (radar detection) * 5660 MHz [132] (17.0 dBm) (radar detection) * 5680 MHz [136] (17.0 dBm) (radar detection) * 5700 MHz [140] (17.0 dBm) (radar detection) * 5745 MHz [149] (27.0 dBm) * 5765 MHz [153] (27.0 dBm) * 5785 MHz [157] (27.0 dBm) * 5805 MHz [161] (27.0 dBm) * 5825 MHz [165] (27.0 dBm) [ ... ]
This output shows that in the 5 GHz band, beaconing is allowed for channels within the 5170–5250 MHz and 5735–5835 MHz ranges.
This will occur if the module was not signed during installation, and if the kernel is configured to enforce module signature verification. To work around this, you will need to disable Secure Boot and/or add a kernel boot parameter: enforcemodulesig=0