Merge pull request #41 from open-sdr/master

sync master
This commit is contained in:
Jiao Xianjun 2020-10-20 16:01:41 +02:00 committed by GitHub
commit c87b459afb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1973 additions and 98 deletions

View File

@ -3,7 +3,9 @@
**openwifi:** Linux mac80211 compatible full-stack IEEE802.11/Wi-Fi design based on SDR (Software Defined Radio).
This repository includes Linux driver and software. [openwifi-hw](https://github.com/open-sdr/openwifi-hw) repository has the FPGA design. [[Quick start](#Quick-start)], [[Project document](doc)], [[Application notes](doc/app_notes)]
This repository includes Linux driver and software. [openwifi-hw](https://github.com/open-sdr/openwifi-hw) repository has the FPGA design.
[[Project document](doc)], [[Quick start](#Quick-start)], [[Application notes](doc/app_notes)]
[[Videos](#Videos)] [[Papers](#Papers)] [openwifi [maillist](https://lists.ugent.be/wws/subscribe/openwifi)] [[Cite openwifi project](#Cite-openwifi-project)]
@ -23,6 +25,8 @@ Openwifi code has dual licenses. AGPLv3 is the opensource license. For non-opens
- Easy to change bandwidth and frequency:
- 2MHz for 802.11ah in sub-GHz
- 10MHz for 802.11p/vehicle in 5.9GHz
- CSI (Channel State Information, freq offset, equalizer to computer) [[CSI notes](doc/app_notes/csi.md)]
- IQ capture (real-time AGC, RSSI, IQ sample to computer) [[IQ notes](doc/app_notes/iq.md)]
- On roadmap: **802.11ax**
**Performance (AP: openwifi at channel 44, client: TL-WDN4200 N900 USB Dongle):**
@ -33,12 +37,12 @@ Openwifi code has dual licenses. AGPLv3 is the opensource license. For non-opens
board_name|board combination|status|SD card img
-------|-------|----|----
zc706_fmcs2|Xilinx ZC706 dev board + FMCOMMS2/3/4|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-4-32bit.img.xz)
zed_fmcs2|Xilinx zed board + FMCOMMS2/3/4|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-4-32bit.img.xz)
adrv9364z7020|ADRV9364-Z7020 + ADRV1CRR-BOB|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-4-32bit.img.xz)
adrv9361z7035|ADRV9361-Z7035 + ADRV1CRR-BOB/FMC|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-4-32bit.img.xz)
zc702_fmcs2|Xilinx ZC702 dev board + FMCOMMS2/3/4|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-4-32bit.img.xz)
zcu102_fmcs2|Xilinx ZCU102 dev board + FMCOMMS2/3/4|Done|[64bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-4-64bit.img.xz)
zc706_fmcs2|Xilinx ZC706 dev board + FMCOMMS2/3/4|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-6-32bit.img.xz)
zed_fmcs2|Xilinx zed board + FMCOMMS2/3/4|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-6-32bit.img.xz)
adrv9364z7020|ADRV9364-Z7020 + ADRV1CRR-BOB|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-6-32bit.img.xz)
adrv9361z7035|ADRV9361-Z7035 + ADRV1CRR-BOB/FMC|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-6-32bit.img.xz)
zc702_fmcs2|Xilinx ZC702 dev board + FMCOMMS2/3/4|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-6-32bit.img.xz)
zcu102_fmcs2|Xilinx ZCU102 dev board + FMCOMMS2/3/4|Done|[64bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-6-64bit.img.xz)
zcu102_9371|Xilinx ZCU102 dev board + ADRV9371|Future|None
- board_name is used to identify FPGA design in openwifi-hw/boards/
@ -83,6 +87,7 @@ zcu102_9371|Xilinx ZCU102 dev board + ADRV9371|Future|None
**ethX** is the PC NIC name connecting the board. **ethY** is the PC NIC name connecting internet.
If you want, uncommenting "net.ipv4.ip_forward=1" in /etc/sysctl.conf to make IP forwarding persistent on PC.
- To monitor **real-time CSI (Chip State Information)**, such as timestamp, frequency offset, channel state, equalizer, please refer to [[CSI notes](doc/app_notes/csi.md)].
## Basic operations
The board actually is an Linux/Ubuntu computer which is running **hostapd** to offer Wi-Fi AP functionality over the Wi-Fi Network Interface (NIC). The NIC is implemented by openwifi-hw FPGA design. We use the term **"On board"** to indicate that the commands should be executed after ssh login to the board. **"On PC"** means the commands should run on PC.
@ -240,9 +245,11 @@ FOSDEM2020 [[youtube](https://youtu.be/Mq48cGthk7M)], [[link for CHN user](https
Low latency for gaming and introduction [[youtube](https://youtu.be/Notn9X482LI)], [[link for CHN user](https://www.zhihu.com/zvideo/1273823153371385856)]
CSI (Channel State Information) [[twitter](https://twitter.com/i/status/1314207380561780738)], [[link for CHN user](https://www.zhihu.com/zvideo/1297662571618148352)]
## Papers
- [openwifi: a free and open-source IEEE802.11 SDR implementation on SoC](https://biblio.ugent.be/publication/8663043/file/8663044.pdf)
- [openwifi: a free and open-source IEEE802.11 SDR implementation on SoC](https://www.orca-project.eu/wp-content/uploads/sites/4/2020/03/openwifi-vtc-antwerp-PID1249076.pdf)
- [csi murder](https://ans.unibs.it/projects/csi-murder/)
Openwifi was born in [ORCA project](https://www.orca-project.eu/) (EU's Horizon2020 programme under agreement number 732174).

View File

@ -3,3 +3,5 @@ Application notes collect many small topics about using openwifi in different sc
- [Use openwifi on the w-iLab.t testbed remotely](https://doc.ilabt.imec.be/ilabt/wilab/tutorials/openwifi.html)
- [Communication between two SDR boards under AP and client mode](ap-client-two-sdr.md)
- [Communication between two SDR boards under ad-hoc mode](ad-hoc-two-sdr.md)
- [From CSI (Channel State Information) to CSI (Chip State Information)](csi.md)
- [Capture IQ sample, AGC gain, RSSI with many types of trigger condition](iq.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

125
doc/app_notes/csi.md Normal file
View File

@ -0,0 +1,125 @@
We extend the **CSI** (Channel State Information) to **CSI** (Chip State Information)!
## Quick start
- Power on the SDR board.
- Connect a computer to the SDR board via Ethernet cable. The computer should have static IP 192.168.10.1. Open a terminal on the computer, and then in the terminal:
```
ssh root@192.168.10.122
(password: openwifi)
cd openwifi
./wgd.sh
(Wait for the script completed)
./monitor_ch.sh sdr0 11
(Monitor on channel 11. You can change 11 to other channel that is busy)
insmod side_ch.ko
./side_ch_ctl g
```
You should see on board outputs like:
```
loop 64 side info count 61
loop 128 side info count 99
...
```
If the second number (61, 99, ...) is not zero and keeps increasing, that means the CSI (Chip State Information) is going to the computer smoothly.
- Open another terminal on the computer, and run:
```
cd openwifi/user_space/side_ch_ctl_src
python3 side_info_display.py
```
The python script needs "matplotlib.pyplot" and "numpy" packages installed. Now you should see 3 figures showing run-time **frequency offset**, **channel state/response** and **constellation form equalizer**. Meanwhile the python script prints the **timestamp**.
While running, all informations are also stored into a file **side_info.txt**. A matlab script **test_side_info_file_display.m** is offered to help you do analysis on the Chip State Information offline.
## Understand the CSI feature
The CSI information is extracted via the openwifi **side channel** infrastructure. This figure explains the related modules (also related source code file name) and how the information goes from the SDR board to the computer.
![](./csi-architecture.jpg)
The CSI information format is shown in this figure.
![](./csi-information-format.jpg)
For each element, the actual size is 64bit.
- timestamp: 64bit TSF timer value, which is the same timestamp value shown by other sniffer software, like tcpdump, wireshark or openwifi printing in dmesg.
- freq_offset: Only the 1st 16bit is used.
- csi (channel state/response) and equalizer: Only the first two 16bit are used for I/Q of channel response and equalizer output. The remaining two 16bit are reserved for future multi-antenna cases.
The python and Matlab scripts are recommended for you to understand the CSI packet format precisely.
## Config the capture condition and interval
The quick start guide will monitor all CSI informations of all packets decoded by the WiFi ofdm receiver. To monitor only specific packets that match the specific conditions: FC (Frame Control), addr1 (target MAC address), addr2 (source MAC address), configuration command should be issued before executing "**side_ch_ctl g**". The configuration command is realized by feeding a different parameter to "**side_ch_ctl**".
A quick example: Capture only CSI of those packets from the device with MAC address 56:5b:01:ec:e2:8f
```
./side_ch_ctl wh1h4001
./side_ch_ctl wh7h01ece28f
(01ece28f are the last 32 bits of MAC address 56:5b:01:ec:e2:8f)
./side_ch_ctl g
```
The parameter string format is explained in detail:
```
whXhY
```
The X is the register index, and the Y is the value in hex format. The remaining "w", "h" and "h" should be kept untouched.
- To turn on conditional capture, X should be 1. For Y: bit11~bit0 should be 001(hex), bit12: on/off of FC match, bit13: on/off of addr1 match, bit14 : on/off of addr2 match. Examples:
```
Turn on FC only match:
./side_ch_ctl wh1h1001
(1001 is the value in hex format)
Turn on addr2 (source address) only match:
./side_ch_ctl wh1h4001
Turn on both FC and addr1 (target address) match:
./side_ch_ctl wh1h3001
Turn off conditional capture (all packets will be captured):
./side_ch_ctl wh1h0001
```
- To specify the condition matching target (when that type of match is turned on by above command):
```
Specify the FC matching target:
./side_ch_ctl wh5hY
(Y is the FC value in hex format)
Specify the addr1 (target address) matching target:
./side_ch_ctl wh6hY
Specify the addr2 (source address) matching target:
./side_ch_ctl wh7hY
(Y is the MAC address in hex format. Only the last 32 bits are needed. Example: for 56:5b:01:ec:e2:8f, 01ece28f should be input.)
```
The command "**side_ch_ctl g**" will perform CSI capture every 100ms until you press ctrl+C. To use a different capture interval:
```
side_ch_ctl gN
```
The interval will become N*1ms
## Config the num_eq
The num_eq (number of equalizer output) is configurable in case you don't need so many equalizer informations. The valid value is 0~8. You should align the num_eq value at the side_ch.ko, side_info_display.py and test_side_info_file_display.m.
- When insert the kernel module, use:
```
insmod side_ch.ko num_eq_init=3
```
You can replace 3 by number 0~8. (8 is the default value. You don't need to specify it like in the Quick start section)
- When launch the python script, use:
```
side_info_display.py 3
```
- When use the Matlab script, please change the num_eq variable in the script to 3 (3 is just an example).
## Compile the side channel driver and user space program
- side_ch.ko
```
$OPENWIFI_DIR/driver/side_ch/make_driver.sh $OPENWIFI_DIR $XILINX_DIR ARCH_BIT
(For Zynq 7000, ARCH_BIT should be 32, for Zynq MPSoC, ARCH_BIT should be 64)
```
- side_ch_ctl (take user_space/side_ch_ctl_src/side_ch_ctl.c and compile it on board!)
```
gcc -o side_ch_ctl side_ch_ctl.c
```
## Run the CSI together with modes other than monitor
The openwifi CSI feature could run with not only monitor mode but also other modes, such as AP-Client or ad-hoc mode. After the communication functionality is fully up in those modes, you can start CSI feature from "**insmod side_ch.ko**" and "**./side_ch_ctl g**" on board as described in the previous sections to extract CSI to your computer.
## Map the CSI information to the WiFi packet
If you want to relate the CSI information to the WiFi packet, you need to capture WiFi packets (tcpdump/wireshark/etc) while capturing CSI. Then you can match the timestamp (TSF timer value) between WiFi packet and CSI information, because this is the unique same identity of a Wifi packet and related CSI information.
Please learn the python and Matlab script to extract CSI information per packet according to your requirement.

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

139
doc/app_notes/iq.md Normal file
View File

@ -0,0 +1,139 @@
We implement the **IQ sample capture** with interesting extensions: many **trigger conditions**; **RSSI**, RF chip **AGC** **status (lock/unlock)** and **gain**.
## Quick start
- Power on the SDR board.
- Connect a computer to the SDR board via Ethernet cable. The computer should have static IP 192.168.10.1. Open a terminal on the computer, and then in the terminal:
```
ssh root@192.168.10.122
(password: openwifi)
cd openwifi
./wgd.sh
(Wait for the script completed)
./monitor_ch.sh sdr0 11
(Monitor on channel 11. You can change 11 to other channel that is busy)
insmod side_ch.ko iq_len_init=8187
(for zed, adrv9364z7020, zc702 board, 8187 should be 4095 because they have smaller FPGA)
./side_ch_ctl wh11d4094
(Above command is needed only when you run with zed, adrv9364z7020, zc702 board)
./side_ch_ctl g
```
You should see on board outputs like:
```
loop 64 side info count 61
loop 128 side info count 99
...
```
If the second number (61, 99, ...) is not zero and keeps increasing, that means the IQ sample is going to the computer smoothly.
- Open another terminal on the computer, and run:
```
cd openwifi/user_space/side_ch_ctl_src
python3 iq_capture.py
(for zed, adrv9364z7020, zc702 board, add 4095 as parameter!)
```
The python script needs "matplotlib.pyplot" and "numpy" packages installed. Now you should see 3 figures showing run-time **IQ sample**, **AGC gain and lock status** and **RSSI (uncalibrated)**. Meanwhile the python script prints the **timestamp**.
While running, all informations are also stored into a file **iq.txt**. A matlab script **test_iq_file_display.m** is offered to help you do analysis on the IQ Information offline. For zed, adrv9364z7020, zc702 board, do not forget to change the **iq_len** in the matlab script to 4095.
## Understand the IQ capture feature
The IQ information is extracted via the openwifi **side channel** infrastructure. This figure explains the related modules (also related source code file name) and how the information goes from the SDR board to the computer.
![](./iq-architecture.jpg)
The IQ information format is shown in this figure.
![](./iq-information-format.jpg)
For each element, the actual size is 64bit.
- timestamp: 64bit TSF timer value when the capture is triggered.
- IQ
- The first two 16bit are used for I/Q sample
- The 3rd 16bit is AD9361 AGC gain (bit7 -- lock/unlock; bit6~0 -- gain value)
- The 4th 16bit is RSSI (half dB, uncalibrated). Please check xpu.v and sdr.c to understand how the raw RSSI value is finally calibrated and reported to Linux mac80211.
The python and Matlab scripts are recommended for you to understand the IQ packet format precisely.
## Config the IQ capture and interval
The quick start guide captures a period of history IQ when the packet FCS checksum is checked by Wifi receiver (no matter pass or fail). To initiate the capture with different trigger conditions and length, configuration commands should be issued before executing "**side_ch_ctl g**". The configuration command is realized by feeding a different parameter to "**side_ch_ctl**". The main parameters that are configurable are explained in this figure.
![](./iq-capture-parameter.jpg)
**iq_len** is the number of IQ samples captured per trigger condition met. The capture is started from the time **pre_trigger_len** IQ samples before the trigger moment. **iq_len** is set only one time when you insert the side_ch.ko. Please check the next section for **iq_len** configuration. This section introduces the setting of pre_trigger_len and trigger condition.
- pre_trigger_len
```
./side_ch_ctl wh11dY
```
The parameter **Y** specifies the pre_trigger_len. Valid range 0 ~ 8190. It is limited by the FPGA fifo size. For **small FPGA** (zed_fmcs2, adrv9364z7020, zc702), the valid range is 0 ~ **4094**.
- trigger condition
```
./side_ch_ctl wh8dY
```
The parameter **Y** specifies the trigger condition. Valid range 0 ~ 15, which is explained in this table.
value|meaning
-----|-------
0 |receiver gives FCS checksum result. no matter pass/fail
1 |receiver gives FCS checksum result. pass
2 |receiver gives FCS checksum result. fail
3 |receiver gives SIGNAL field checksum result. no matter pass/fail
4 |receiver gives SIGNAL field checksum result. pass
5 |receiver gives SIGNAL field checksum result. fail
6 |receiver gives SIGNAL field checksum result. no matter pass/fail. HT packet
7 |receiver gives SIGNAL field checksum result. no matter pass/fail. non-HT packet
8 |receiver gives long preamble detected
9 |receiver gives short preamble detected
10|RSSI (half dB uncalibrated) goes above the threshold
11|RSSI (half dB uncalibrated) goes below the threshold
12|AD9361 AGC from lock to unlock
13|AD9361 AGC from unlock to lock
14|AD9361 AGC gain goes above the threshold
15|AD9361 AGC gain goes below the threshold
To set the RSSI threshold
```
./side_ch_ctl wh9dY
```
The parameter **Y** specifies the RSSI threshold. Valid range 0 ~ 2047.
To set the AGC gain threshold
```
./side_ch_ctl wh10dY
```
The parameter **Y** specifies the AGC gain threshold. Valid range 0 ~ 127.
The command "**side_ch_ctl g**" will perform IQ capture every 100ms until you press ctrl+C. To use a different capture interval:
```
side_ch_ctl gN
```
The interval will become N*1ms
## Config the iq_len
The **iq_len** (number of IQ sample per capture) is configurable in case you want less IQ samples per capture so that it can be triggered more times during a specific analysis period. The valid value is 1~**8187**. For **small FPGA** (zed_fmcs2, adrv9364z7020, zc702), the valid range is 0 ~ **4095**. It is independant from pre_trigger_len, and it can be less than pre_trigger_len if you want. You should align the **iq_len** value at the side_ch.ko, iq_capture.py and test_iq_file_display.m.
- When insert the kernel module, use:
```
insmod side_ch.ko iq_len_init=3000
```
Here 3000 is an example. **ATTENTION:** You need to specify **iq_len_init** explicitly to turn on IQ capture, which will turn off CSI. Insert the side_ch.ko without any parameter will run CSI mode.
- When launch the python script, use:
```
python3 iq_capture.py 3000
```
- When use the matlab script, please change the **iq_len** variable in the script to 3000.
## Compile the side channel driver and user space program
- side_ch.ko
```
$OPENWIFI_DIR/driver/side_ch/make_driver.sh $OPENWIFI_DIR $XILINX_DIR ARCH_BIT
(For Zynq 7000, ARCH_BIT should be 32, for Zynq MPSoC, ARCH_BIT should be 64)
```
- side_ch_ctl (take user_space/side_ch_ctl_src/side_ch_ctl.c and compile it on board!)
```
gcc -o side_ch_ctl side_ch_ctl.c
```
## Run the IQ capture together with modes other than monitor
The openwifi IQ capture feature could run with not only monitor mode but also other modes, such as AP-Client or ad-hoc mode. After the communication functionality is fully up in those modes, you can start IQ capture from "**insmod side_ch.ko**" and "**./side_ch_ctl g**" on board as described in the previous sections to extract IQ information to your computer.
## Map the IQ information to the WiFi packet
If you want to relate the IQ information to the WiFi packet, you need to capture WiFi packets (tcpdump/wireshark/etc) while capturing IQ. Then you can relate the timestamp between WiFi packet and IQ information. Please be noticed that the timestamp in the IQ information is the moment when capture is triggered, which could be different from the timestamp reported in the packet capture program. But since they share the same time base (TSF timer), you can relate them easily by analyzing the WiFi packet and IQ sample sequence.
Please learn the python and Matlab script to extract IQ information per capture according to your requirement.

View File

@ -123,7 +123,7 @@ static void ad9361_rf_set_channel(struct ieee80211_hw *dev,
struct ieee80211_conf *conf)
{
struct openwifi_priv *priv = dev->priv;
u32 actual_rx_lo = conf->chandef.chan->center_freq - priv->rx_freq_offset_to_lo_MHz;
u32 actual_rx_lo = conf->chandef.chan->center_freq - priv->rx_freq_offset_to_lo_MHz + priv->drv_rx_reg_val[DRV_RX_REG_IDX_EXTRA_FO];
u32 actual_tx_lo;
bool change_flag = (actual_rx_lo != priv->actual_rx_lo);
@ -810,7 +810,7 @@ static void openwifi_tx(struct ieee80211_hw *dev,
goto openwifi_tx_early_out_after_lock;
}
// sg_init_table(&tx_sg, 1); // only need to be initialized once in openwifi_start
sg_init_table(&(priv->tx_sg), 1); // only need to be initialized once in openwifi_start
sg_dma_address( &(priv->tx_sg) ) = dma_mapping_addr;
sg_dma_len( &(priv->tx_sg) ) = num_dma_byte;
@ -969,7 +969,7 @@ static int openwifi_start(struct ieee80211_hw *dev)
printk("%s openwifi_start: rx_intf_cfg %d openofdm_rx_cfg %d tx_intf_cfg %d openofdm_tx_cfg %d\n",sdr_compatible_str, priv->rx_intf_cfg, priv->openofdm_rx_cfg, priv->tx_intf_cfg, priv->openofdm_tx_cfg);
printk("%s openwifi_start: rx_freq_offset_to_lo_MHz %d tx_freq_offset_to_lo_MHz %d\n",sdr_compatible_str, priv->rx_freq_offset_to_lo_MHz, priv->tx_freq_offset_to_lo_MHz);
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x3004F); //disable tx interrupt
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x30004); //disable tx interrupt
rx_intf_api->RX_INTF_REG_INTERRUPT_TEST_write(0x100); // disable rx interrupt by interrupt test mode
rx_intf_api->RX_INTF_REG_M_AXIS_RST_write(1); // hold M AXIS in reset status
@ -1033,7 +1033,7 @@ static int openwifi_start(struct ieee80211_hw *dev)
}
rx_intf_api->RX_INTF_REG_INTERRUPT_TEST_write(0x000); // enable rx interrupt get normal fcs valid pass through ddc to ARM
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x4F); //enable tx interrupt
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x4); //enable tx interrupt
rx_intf_api->RX_INTF_REG_M_AXIS_RST_write(0); // release M AXIS
xpu_api->XPU_REG_TSF_LOAD_VAL_write(0,0); // reset tsf timer
@ -1080,7 +1080,7 @@ static void openwifi_stop(struct ieee80211_hw *dev)
//ieee80211_stop_queue(dev, 0);
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x3004F); //disable tx interrupt
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x30004); //disable tx interrupt
rx_intf_api->RX_INTF_REG_INTERRUPT_TEST_write(0x100); // disable fcs_valid by interrupt test mode
rx_intf_api->RX_INTF_REG_M_AXIS_RST_write(1); // hold M AXIS in reset status

View File

@ -68,15 +68,16 @@ union u16_byte2 {
#define OPENWIFI_LED_MAX_NAME_LEN 32
// ------------ software reg definition ------------
#define MAX_NUM_DRV_REG 8
#define DRV_TX_REG_IDX_RATE 0
#define MAX_NUM_DRV_REG 8
#define DRV_TX_REG_IDX_RATE 0
#define DRV_TX_REG_IDX_FREQ_BW_CFG 1
#define DRV_TX_REG_IDX_PRINT_CFG (MAX_NUM_DRV_REG-1)
#define DRV_TX_REG_IDX_PRINT_CFG (MAX_NUM_DRV_REG-1)
#define DRV_RX_REG_IDX_FREQ_BW_CFG 1
#define DRV_RX_REG_IDX_PRINT_CFG (MAX_NUM_DRV_REG-1)
#define DRV_RX_REG_IDX_EXTRA_FO 2
#define DRV_RX_REG_IDX_PRINT_CFG (MAX_NUM_DRV_REG-1)
#define DRV_XPU_REG_IDX_GIT_REV (MAX_NUM_DRV_REG-1)
#define DRV_XPU_REG_IDX_GIT_REV (MAX_NUM_DRV_REG-1)
// ------end of software reg definition ------------

11
driver/side_ch/Makefile Normal file
View File

@ -0,0 +1,11 @@
# by Xianjun jiao. putaoshu@msn.com; xianjun.jiao@imec.be
obj-m += side_ch.o
# obj-m += axidmatest.o
all:
make -C $(KDIR) M=$(PWD) modules
# ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

58
driver/side_ch/make_driver.sh Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
if [ "$#" -ne 3 ]; then
echo "You must enter exactly 3 arguments: \$OPENWIFI_DIR \$XILINX_DIR ARCH_BIT(32 or 64)"
exit 1
fi
OPENWIFI_DIR=$1
XILINX_DIR=$2
ARCH_OPTION=$3
if [ -f "$OPENWIFI_DIR/LICENSE" ]; then
echo "\$OPENWIFI_DIR is found!"
else
echo "\$OPENWIFI_DIR is not correct. Please check!"
exit 1
fi
if [ -d "$XILINX_DIR/SDK" ]; then
echo "\$XILINX_DIR is found!"
else
echo "\$XILINX_DIR is not correct. Please check!"
exit 1
fi
if [ "$ARCH_OPTION" != "32" ] && [ "$ARCH_OPTION" != "64" ]; then
echo "\$ARCH_OPTION is not correct. Should be 32 or 64. Please check!"
exit 1
else
echo "\$ARCH_OPTION is valid!"
fi
source $XILINX_DIR/SDK/2018.3/settings64.sh
if [ "$ARCH_OPTION" == "64" ]; then
LINUX_KERNEL_SRC_DIR=$OPENWIFI_DIR/adi-linux-64/
ARCH="arm64"
CROSS_COMPILE="aarch64-linux-gnu-"
else
LINUX_KERNEL_SRC_DIR=$OPENWIFI_DIR/adi-linux/
ARCH="arm"
CROSS_COMPILE="arm-linux-gnueabihf-"
fi
# check if user entered the right path to analog device linux
if [ -d "$LINUX_KERNEL_SRC_DIR" ]; then
echo " setup linux kernel path ${LINUX_KERNEL_SRC_DIR}"
else
echo "Error: path to adi linux: ${LINUX_KERNEL_SRC_DIR} not found. Can not continue."
exit 1
fi
set -x
home_dir=$(pwd)
cd $OPENWIFI_DIR/driver/side_ch
make KDIR=$LINUX_KERNEL_SRC_DIR ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE
cd $home_dir

677
driver/side_ch/side_ch.c Normal file
View File

@ -0,0 +1,677 @@
/*
* openwifi side channel driver
* Xianjun jiao. putaoshu@msn.com; xianjun.jiao@imec.be
*/
#include <linux/bitops.h>
#include <linux/dmapool.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_dma.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include "side_ch.h"
static int num_eq_init = 8; // should be 0~8
static int iq_len_init = 0; //if iq_len>0, iq capture enabled, csi disabled
module_param(num_eq_init, int, 0);
MODULE_PARM_DESC(num_eq_init, "num_eq_init. 0~8. number of equalizer output (52 each) appended to CSI");
module_param(iq_len_init, int, 0);
MODULE_PARM_DESC(iq_len_init, "iq_len_init. if iq_len_init>0, iq capture enabled, csi disabled");
static void __iomem *base_addr; // to store driver specific base address needed for mmu to translate virtual address to physical address in our FPGA design
struct dma_chan *chan_to_pl = NULL;
struct dma_chan *chan_to_ps = NULL;
u8 *side_info_buf = NULL;
dma_cookie_t chan_to_ps_cookie;
const int max_side_info_buf_size = MAX_NUM_DMA_SYMBOL*8;
/* IO accessors */
static inline u32 reg_read(u32 reg)
{
return ioread32(base_addr + reg);
}
static inline void reg_write(u32 reg, u32 value)
{
iowrite32(value, base_addr + reg);
}
static inline void SIDE_CH_REG_MULTI_RST_write(u32 Data) {
reg_write(SIDE_CH_REG_MULTI_RST_ADDR, Data);
}
static inline u32 SIDE_CH_REG_CONFIG_read(void){
return reg_read(SIDE_CH_REG_CONFIG_ADDR);
}
static inline void SIDE_CH_REG_CONFIG_write(u32 value){
reg_write(SIDE_CH_REG_CONFIG_ADDR, value);
}
static inline u32 SIDE_CH_REG_NUM_DMA_SYMBOL_read(void){
return reg_read(SIDE_CH_REG_NUM_DMA_SYMBOL_ADDR);
}
static inline void SIDE_CH_REG_NUM_DMA_SYMBOL_write(u32 value){
reg_write(SIDE_CH_REG_NUM_DMA_SYMBOL_ADDR, value);
}
static inline u32 SIDE_CH_REG_IQ_CAPTURE_read(void){
return reg_read(SIDE_CH_REG_IQ_CAPTURE_ADDR);
}
static inline void SIDE_CH_REG_IQ_CAPTURE_write(u32 value){
reg_write(SIDE_CH_REG_IQ_CAPTURE_ADDR, value);
}
static inline u32 SIDE_CH_REG_NUM_EQ_read(void){
return reg_read(SIDE_CH_REG_NUM_EQ_ADDR);
}
static inline void SIDE_CH_REG_NUM_EQ_write(u32 value){
reg_write(SIDE_CH_REG_NUM_EQ_ADDR, value);
}
static inline u32 SIDE_CH_REG_FC_TARGET_read(void){
return reg_read(SIDE_CH_REG_FC_TARGET_ADDR);
}
static inline void SIDE_CH_REG_FC_TARGET_write(u32 value){
reg_write(SIDE_CH_REG_FC_TARGET_ADDR, value);
}
static inline u32 SIDE_CH_REG_ADDR1_TARGET_read(void){
return reg_read(SIDE_CH_REG_ADDR1_TARGET_ADDR);
}
static inline void SIDE_CH_REG_ADDR1_TARGET_write(u32 value){
reg_write(SIDE_CH_REG_ADDR1_TARGET_ADDR, value);
}
static inline u32 SIDE_CH_REG_ADDR2_TARGET_read(void){
return reg_read(SIDE_CH_REG_ADDR2_TARGET_ADDR);
}
static inline void SIDE_CH_REG_ADDR2_TARGET_write(u32 value){
reg_write(SIDE_CH_REG_ADDR2_TARGET_ADDR, value);
}
static inline u32 SIDE_CH_REG_IQ_TRIGGER_read(void){
return reg_read(SIDE_CH_REG_IQ_TRIGGER_ADDR);
}
static inline void SIDE_CH_REG_IQ_TRIGGER_write(u32 value){
reg_write(SIDE_CH_REG_IQ_TRIGGER_ADDR, value);
}
static inline u32 SIDE_CH_REG_RSSI_TH_read(void){
return reg_read(SIDE_CH_REG_RSSI_TH_ADDR);
}
static inline void SIDE_CH_REG_RSSI_TH_write(u32 value){
reg_write(SIDE_CH_REG_RSSI_TH_ADDR, value);
}
static inline u32 SIDE_CH_REG_GAIN_TH_read(void){
return reg_read(SIDE_CH_REG_GAIN_TH_ADDR);
}
static inline void SIDE_CH_REG_GAIN_TH_write(u32 value){
reg_write(SIDE_CH_REG_GAIN_TH_ADDR, value);
}
static inline u32 SIDE_CH_REG_PRE_TRIGGER_LEN_read(void){
return reg_read(SIDE_CH_REG_PRE_TRIGGER_LEN_ADDR);
}
static inline void SIDE_CH_REG_PRE_TRIGGER_LEN_write(u32 value){
reg_write(SIDE_CH_REG_PRE_TRIGGER_LEN_ADDR, value);
}
static inline u32 SIDE_CH_REG_IQ_LEN_read(void){
return reg_read(SIDE_CH_REG_IQ_LEN_ADDR);
}
static inline void SIDE_CH_REG_IQ_LEN_write(u32 value){
reg_write(SIDE_CH_REG_IQ_LEN_ADDR, value);
}
static inline u32 SIDE_CH_REG_M_AXIS_DATA_COUNT_read(void){
return reg_read(SIDE_CH_REG_M_AXIS_DATA_COUNT_ADDR);
}
static inline void SIDE_CH_REG_M_AXIS_DATA_COUNT_write(u32 value){
reg_write(SIDE_CH_REG_M_AXIS_DATA_COUNT_ADDR, value);
}
static const struct of_device_id dev_of_ids[] = {
{ .compatible = "sdr,side_ch", },
{}
};
MODULE_DEVICE_TABLE(of, dev_of_ids);
static void chan_to_ps_callback(void *completion)
{
complete(completion);
}
#if 0
static void chan_to_pl_callback(void *completion)
{
complete(completion);
}
static int dma_loopback_test(int num_test, int num_dma_symbol) {
int i, err = 0;
// -----------dma loop back test-------------------------
enum dma_status status;
enum dma_ctrl_flags flags;
u8 *src_buf, *dst_buf;
// int num_dma_symbol = 16;
int test_buf_size = num_dma_symbol*8;
dma_addr_t src_buf_dma;
dma_addr_t dst_buf_dma;
struct dma_device *chan_to_pl_dev = chan_to_pl->device;
struct dma_device *chan_to_ps_dev = chan_to_ps->device;
struct scatterlist chan_to_pl_sg[1];
struct scatterlist chan_to_ps_sg[1];
dma_cookie_t chan_to_pl_cookie;
dma_cookie_t chan_to_ps_cookie;
struct completion chan_to_pl_cmp;
struct completion chan_to_ps_cmp;
struct dma_async_tx_descriptor *chan_to_pl_d = NULL;
struct dma_async_tx_descriptor *chan_to_ps_d = NULL;
unsigned long chan_to_ps_tmo = msecs_to_jiffies(300000);
unsigned long chan_to_pl_tmo = msecs_to_jiffies(30000);
int test_idx;
for (test_idx=0; test_idx<num_test; test_idx++) {
printk("%s test_idx %d\n", side_ch_compatible_str, test_idx);
//set number of dma symbols expected to pl and ps
SIDE_CH_REG_NUM_DMA_SYMBOL_write((num_dma_symbol<<16)|num_dma_symbol);
src_buf = kmalloc(test_buf_size, GFP_KERNEL);
if (!src_buf)
goto err_src_buf;
dst_buf = kmalloc(test_buf_size, GFP_KERNEL);
if (!dst_buf)
goto err_dst_buf;
// test buf init
for (i=0; i<test_buf_size; i++) {
src_buf[i] = (test_idx+test_buf_size-i-1);
dst_buf[i] = 0;
}
set_user_nice(current, 10);
flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
src_buf_dma = dma_map_single(chan_to_pl_dev->dev, src_buf, test_buf_size, DMA_MEM_TO_DEV);
if (dma_mapping_error(chan_to_pl_dev->dev, src_buf_dma)) {
printk("%s dma_loopback_test WARNING chan_to_pl_dev DMA mapping error\n", side_ch_compatible_str);
goto err_src_buf_dma_mapping;
}
dst_buf_dma = dma_map_single(chan_to_ps_dev->dev, dst_buf, test_buf_size, DMA_DEV_TO_MEM);
if (dma_mapping_error(chan_to_ps_dev->dev, dst_buf_dma)) {
printk("%s dma_loopback_test WARNING chan_to_ps_dev DMA mapping error\n", side_ch_compatible_str);
goto err_dst_buf_dma_mapping;
}
sg_init_table(chan_to_ps_sg, 1);
sg_init_table(chan_to_pl_sg, 1);
sg_dma_address(&chan_to_ps_sg[0]) = dst_buf_dma;
sg_dma_address(&chan_to_pl_sg[0]) = src_buf_dma;
sg_dma_len(&chan_to_ps_sg[0]) = test_buf_size;
sg_dma_len(&chan_to_pl_sg[0]) = test_buf_size;
chan_to_ps_d = chan_to_ps_dev->device_prep_slave_sg(chan_to_ps, chan_to_ps_sg, 1, DMA_DEV_TO_MEM, flags, NULL);
chan_to_pl_d = chan_to_pl_dev->device_prep_slave_sg(chan_to_pl, chan_to_pl_sg, 1, DMA_MEM_TO_DEV, flags, NULL);
if (!chan_to_ps_d || !chan_to_pl_d) {
printk("%s dma_loopback_test WARNING !chan_to_ps_d || !chan_to_pl_d\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
}
init_completion(&chan_to_pl_cmp);
chan_to_pl_d->callback = chan_to_pl_callback;
chan_to_pl_d->callback_param = &chan_to_pl_cmp;
chan_to_pl_cookie = chan_to_pl_d->tx_submit(chan_to_pl_d);
init_completion(&chan_to_ps_cmp);
chan_to_ps_d->callback = chan_to_ps_callback;
chan_to_ps_d->callback_param = &chan_to_ps_cmp;
chan_to_ps_cookie = chan_to_ps_d->tx_submit(chan_to_ps_d);
if (dma_submit_error(chan_to_pl_cookie) || dma_submit_error(chan_to_ps_cookie)) {
printk("%s dma_loopback_test WARNING dma_submit_error\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
}
dma_async_issue_pending(chan_to_pl);
dma_async_issue_pending(chan_to_ps);
chan_to_pl_tmo = wait_for_completion_timeout(&chan_to_pl_cmp, chan_to_pl_tmo);
status = dma_async_is_tx_complete(chan_to_pl, chan_to_pl_cookie, NULL, NULL);
if (chan_to_pl_tmo == 0) {
printk("%s dma_loopback_test chan_to_pl_tmo == 0\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
} else if (status != DMA_COMPLETE) {
printk("%s dma_loopback_test chan_to_pl status != DMA_COMPLETE\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
}
chan_to_ps_tmo = wait_for_completion_timeout(&chan_to_ps_cmp, chan_to_ps_tmo);
status = dma_async_is_tx_complete(chan_to_ps, chan_to_ps_cookie, NULL, NULL);
if (chan_to_ps_tmo == 0) {
printk("%s dma_loopback_test chan_to_ps_tmo == 0\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
} else if (status != DMA_COMPLETE) {
printk("%s dma_loopback_test chan_to_ps status != DMA_COMPLETE\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
}
dma_unmap_single(chan_to_pl_dev->dev, src_buf_dma, test_buf_size, DMA_MEM_TO_DEV);
dma_unmap_single(chan_to_ps_dev->dev, dst_buf_dma, test_buf_size, DMA_DEV_TO_MEM);
// test buf verification
for (i=0; i<test_buf_size; i++) {
//printk("%d ", dst_buf[i]);
if ( dst_buf[i] != ((test_idx+test_buf_size-i-1)%256) )
break;
}
printk("\n");
printk("%s dma_loopback_test buf verification end idx %d (test_buf_size %d)\n", side_ch_compatible_str, i, test_buf_size);
kfree(src_buf);
kfree(dst_buf);
}
printk("%s dma_loopback_test err %d\n", side_ch_compatible_str, err);
return(err);
err_dst_buf_with_unmap:
dma_unmap_single(chan_to_ps_dev->dev, dst_buf_dma, test_buf_size, DMA_DEV_TO_MEM);
err_dst_buf_dma_mapping:
dma_unmap_single(chan_to_pl_dev->dev, src_buf_dma, test_buf_size, DMA_MEM_TO_DEV);
err_src_buf_dma_mapping:
err_dst_buf:
err = -4;
kfree((void*)dst_buf);
err_src_buf:
err = -3;
kfree(src_buf);
return(err);
}
#endif
static int init_side_channel(void) {
side_info_buf = kmalloc(max_side_info_buf_size, GFP_KERNEL);
if (!side_info_buf)
return(-1);
return(0);
}
static int get_side_info(int num_eq, int iq_len) {
// int err = 0;//, i;
struct scatterlist chan_to_ps_sg[1];
enum dma_status status;
enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int num_dma_symbol, num_dma_symbol_per_trans, side_info_buf_size;
dma_addr_t side_info_buf_dma;
struct dma_device *chan_to_ps_dev = chan_to_ps->device;
struct completion chan_to_ps_cmp;
struct dma_async_tx_descriptor *chan_to_ps_d = NULL;
unsigned long chan_to_ps_tmo = msecs_to_jiffies(100);
if (side_info_buf==NULL) {
printk("%s get_side_info WARNING side_info_buf==NULL\n", side_ch_compatible_str);
return(-1);
}
status = dma_async_is_tx_complete(chan_to_ps, chan_to_ps_cookie, NULL, NULL);
if (status!=DMA_COMPLETE) {
printk("%s get_side_info WARNING status!=DMA_COMPLETE\n", side_ch_compatible_str);
return(-1);
}
set_user_nice(current, 10);
if (iq_len>0)
num_dma_symbol_per_trans = 1+iq_len;
else
num_dma_symbol_per_trans = HEADER_LEN + CSI_LEN + num_eq*EQUALIZER_LEN;
//set number of dma symbols expected to ps
num_dma_symbol = SIDE_CH_REG_M_AXIS_DATA_COUNT_read();
printk("%s get_side_info m axis data count %d per trans %d\n", side_ch_compatible_str, num_dma_symbol, num_dma_symbol_per_trans);
num_dma_symbol = num_dma_symbol_per_trans*(num_dma_symbol/num_dma_symbol_per_trans);
printk("%s get_side_info actual num dma symbol %d\n", side_ch_compatible_str, num_dma_symbol);
if (num_dma_symbol == 0)
return(-2);
side_info_buf_size = num_dma_symbol*8;
side_info_buf_dma = dma_map_single(chan_to_ps_dev->dev, side_info_buf, side_info_buf_size, DMA_DEV_TO_MEM);
if (dma_mapping_error(chan_to_ps_dev->dev, side_info_buf_dma)) {
printk("%s get_side_info WARNING chan_to_ps_dev DMA mapping error\n", side_ch_compatible_str);
return(-3);
}
sg_init_table(chan_to_ps_sg, 1);
sg_dma_address(&chan_to_ps_sg[0]) = side_info_buf_dma;
sg_dma_len(&chan_to_ps_sg[0]) = side_info_buf_size;
chan_to_ps_d = chan_to_ps_dev->device_prep_slave_sg(chan_to_ps, chan_to_ps_sg, 1, DMA_DEV_TO_MEM, flags, NULL);
if (!chan_to_ps_d) {
printk("%s get_side_info WARNING !chan_to_ps_d\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
}
init_completion(&chan_to_ps_cmp);
chan_to_ps_d->callback = chan_to_ps_callback;
chan_to_ps_d->callback_param = &chan_to_ps_cmp;
chan_to_ps_cookie = chan_to_ps_d->tx_submit(chan_to_ps_d);
if (dma_submit_error(chan_to_ps_cookie)) {
printk("%s get_side_info WARNING dma_submit_error\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
}
SIDE_CH_REG_NUM_DMA_SYMBOL_write(num_dma_symbol); //dma from fpga will start automatically
dma_async_issue_pending(chan_to_ps);
chan_to_ps_tmo = wait_for_completion_timeout(&chan_to_ps_cmp, chan_to_ps_tmo);
status = dma_async_is_tx_complete(chan_to_ps, chan_to_ps_cookie, NULL, NULL);
if (chan_to_ps_tmo == 0) {
printk("%s get_side_info WARNING chan_to_ps_tmo == 0\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
} else if (status != DMA_COMPLETE) {
printk("%s get_side_info WARNING chan_to_ps status != DMA_COMPLETE\n", side_ch_compatible_str);
goto err_dst_buf_with_unmap;
}
dma_unmap_single(chan_to_ps_dev->dev, side_info_buf_dma, side_info_buf_size, DMA_DEV_TO_MEM);
return(side_info_buf_size);
err_dst_buf_with_unmap:
dma_unmap_single(chan_to_ps_dev->dev, side_info_buf_dma, side_info_buf_size, DMA_DEV_TO_MEM);
return(-100);
}
// -----------------netlink recv and send-----------------
// should align with side_ch_ctl.c in user_space
#define ACTION_INVALID 0
#define ACTION_REG_WRITE 1
#define ACTION_REG_READ 2
#define ACTION_SIDE_INFO_GET 3
#define REG_TYPE_INVALID 0
#define REG_TYPE_HARDWARE 1
#define REG_TYPE_SOFTWARE 2
// #define NETLINK_USER 31
struct sock *nl_sk = NULL;
static void side_ch_nl_recv_msg(struct sk_buff *skb) {
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
int *msg=(int*)side_info_buf;
int action_flag, reg_type, reg_idx;
u32 reg_val, *cmd_buf;
int res;
// printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
// msg_size=strlen(msg);
nlh=(struct nlmsghdr*)skb->data;
cmd_buf = (u32*)nlmsg_data(nlh);
// printk(KERN_INFO "Netlink received msg payload:%s\n",(char*)nlmsg_data(nlh));
action_flag = cmd_buf[0];
reg_type = cmd_buf[1];
reg_idx = cmd_buf[2];
reg_val = cmd_buf[3];
printk("%s recv msg: len %d action_flag %d reg_type %d reg_idx %d reg_val %u\n", side_ch_compatible_str, nlmsg_len(nlh), action_flag, reg_type, reg_idx, reg_val);
pid = nlh->nlmsg_pid; /*pid of sending process */
if (action_flag==ACTION_SIDE_INFO_GET) {
res = get_side_info(num_eq_init, iq_len_init);
printk(KERN_INFO "%s recv msg: get_side_info(%d,%d) res %d\n", side_ch_compatible_str, num_eq_init, iq_len_init, res);
if (res>0) {
msg_size = res;
// printk("%s recv msg: %d %d %d %d %d %d %d %d\n", side_ch_compatible_str, msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6], msg[7]);
} else {
msg_size = 4;
msg[0] = -2;
}
} else if (action_flag==ACTION_REG_READ) {
msg_size = 4;
// if (reg_idx<0 || reg_idx>31) {
// msg[0] = -3;
// printk("%s recv msg: invalid reg_idx\n", side_ch_compatible_str);
// } else {
msg[0] = reg_read(reg_idx*4);
// }
} else if (action_flag==ACTION_REG_WRITE) {
msg_size = 4;
// if (reg_idx<0 || reg_idx>31) {
// msg[0] = -4;
// printk("%s recv msg: invalid reg_idx\n", side_ch_compatible_str);
// } else {
msg[0] = 0;
reg_write(reg_idx*4, reg_val);
// }
} else {
msg_size = 4;
msg[0] = -1;
printk("%s recv msg: invalid action_flag\n", side_ch_compatible_str);
}
skb_out = nlmsg_new(msg_size,0);
if(!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh=nlmsg_put(skb_out,0,0,NLMSG_DONE,msg_size,0);
NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
memcpy(nlmsg_data(nlh),msg,msg_size);
res=nlmsg_unicast(nl_sk,skb_out,pid);
if(res<0)
printk(KERN_INFO "Error while sending bak to user\n");
}
static int dev_probe(struct platform_device *pdev) {
struct netlink_kernel_cfg cfg = {
.input = side_ch_nl_recv_msg,
};
struct device_node *np = pdev->dev.of_node;
struct resource *io;
int err=1, i;
printk("\n");
if (np) {
const struct of_device_id *match;
match = of_match_node(dev_of_ids, np);
if (match) {
printk("%s dev_probe: match!\n", side_ch_compatible_str);
err = 0;
}
}
if (err)
return err;
/* Request and map I/O memory */
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base_addr = devm_ioremap_resource(&pdev->dev, io);
if (IS_ERR(base_addr))
return PTR_ERR(base_addr);
printk("%s dev_probe: io start 0x%p end 0x%p name %s flags 0x%08x desc %s\n", side_ch_compatible_str, (void*)io->start, (void*)io->end, io->name, (u32)io->flags, (char*)io->desc);
printk("%s dev_probe: base_addr 0x%p\n", side_ch_compatible_str, base_addr);
printk("%s dev_probe: succeed!\n", side_ch_compatible_str);
// --------------initialize netlink--------------
//nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USERSOCK, &cfg);
if(!nl_sk) {
printk(KERN_ALERT "%s dev_probe: Error creating socket.\n", side_ch_compatible_str);
return -10;
}
//-----------------initialize fpga----------------
printk("%s dev_probe: num_eq_init %d iq_len_init %d\n",side_ch_compatible_str, num_eq_init, iq_len_init);
// disable potential any action from side channel
SIDE_CH_REG_MULTI_RST_write(4);
// SIDE_CH_REG_CONFIG_write(0X6001); // match addr1 and addr2; bit12 FC; bit13 addr1; bit14 addr2
SIDE_CH_REG_CONFIG_write(0x7001); // the most strict condition to prevent side channel action
SIDE_CH_REG_IQ_TRIGGER_write(10); // set iq trigger to rssi, which will never happen when rssi_th is 0
SIDE_CH_REG_NUM_EQ_write(num_eq_init); // capture CSI + 8*equalizer by default
if (iq_len_init>0) {//initialize the side channel into iq capture mode
//Max UDP 65507 bytes; (65507/8)-1 = 8187
if (iq_len_init>8187) {
iq_len_init = 8187;
printk("%s dev_probe: limit iq_len_init to 8187!\n",side_ch_compatible_str);
}
SIDE_CH_REG_IQ_CAPTURE_write(1);
SIDE_CH_REG_PRE_TRIGGER_LEN_write(8190);
SIDE_CH_REG_IQ_LEN_write(iq_len_init);
SIDE_CH_REG_IQ_TRIGGER_write(0); // trigger is set to fcs ok/nok (both)
}
SIDE_CH_REG_CONFIG_write(0x0001); // allow all packets by default; bit12 FC; bit13 addr1; bit14 addr2
//rst
for (i=0;i<8;i++)
SIDE_CH_REG_MULTI_RST_write(0);
for (i=0;i<32;i++)
SIDE_CH_REG_MULTI_RST_write(0xFFFFFFFF);
for (i=0;i<8;i++)
SIDE_CH_REG_MULTI_RST_write(0);
// chan_to_pl = dma_request_slave_channel(&(pdev->dev), "rx_dma_mm2s");
// if (IS_ERR(chan_to_pl)) {
// err = PTR_ERR(chan_to_pl);
// pr_err("%s dev_probe: No channel to PL. %d\n",side_ch_compatible_str,err);
// goto free_chan_to_pl;
// }
chan_to_ps = dma_request_slave_channel(&(pdev->dev), "tx_dma_s2mm");
if (IS_ERR(chan_to_ps)) {
err = PTR_ERR(chan_to_ps);
pr_err("%s dev_probe: No channel to PS. %d\n",side_ch_compatible_str,err);
goto free_chan_to_ps;
}
printk("%s dev_probe: DMA channel setup successfully. chan_to_pl 0x%p chan_to_ps 0x%p\n",side_ch_compatible_str, chan_to_pl, chan_to_ps);
// res = dma_loopback_test(3, 512);
// printk(KERN_INFO "dma_loopback_test(3, 512) res %d\n", res);
err = init_side_channel();
printk("%s dev_probe: init_side_channel() err %d\n",side_ch_compatible_str, err);
return(err);
// err = dma_loopback_test(7, 512);
// if (err == 0)
// return(err);
// else
// dma_release_channel(chan_to_ps);
free_chan_to_ps:
err = -2;
dma_release_channel(chan_to_ps);
return err;
// free_chan_to_pl:
// err = -1;
// dma_release_channel(chan_to_pl);
// return err;
}
static int dev_remove(struct platform_device *pdev)
{
printk("\n");
printk("%s dev_remove: release nl_sk\n", side_ch_compatible_str);
netlink_kernel_release(nl_sk);
pr_info("%s dev_remove: dropped chan_to_pl 0x%p\n", side_ch_compatible_str, chan_to_pl);
if (chan_to_pl != NULL) {
pr_info("%s dev_remove: dropped channel %s\n", side_ch_compatible_str, dma_chan_name(chan_to_pl));
// dmaengine_terminate_all(chan_to_pl); //this also terminate sdr.ko. do not use
dma_release_channel(chan_to_pl);
}
pr_info("%s dev_remove: dropped chan_to_ps 0x%p\n", side_ch_compatible_str, chan_to_ps);
if (chan_to_pl != NULL) {
pr_info("%s dev_remove: dropped channel %s\n", side_ch_compatible_str, dma_chan_name(chan_to_ps));
// dmaengine_terminate_all(chan_to_ps); //this also terminate sdr.ko. do not use
dma_release_channel(chan_to_ps);
}
if (side_info_buf != NULL)
kfree(side_info_buf);
printk("%s dev_remove: base_addr 0x%p\n", side_ch_compatible_str, base_addr);
printk("%s dev_remove: succeed!\n", side_ch_compatible_str);
return 0;
}
static struct platform_driver dev_driver = {
.driver = {
.name = "sdr,side_ch",
.owner = THIS_MODULE,
.of_match_table = dev_of_ids,
},
.probe = dev_probe,
.remove = dev_remove,
};
module_platform_driver(dev_driver);
MODULE_AUTHOR("Xianjun Jiao");
MODULE_DESCRIPTION("sdr,side_ch");
MODULE_LICENSE("GPL v2");

27
driver/side_ch/side_ch.h Normal file
View File

@ -0,0 +1,27 @@
// Xianjun jiao. putaoshu@msn.com; xianjun.jiao@imec.be
// ---------------------------------------side channel-------------------------------
const char *side_ch_compatible_str = "sdr,side_ch";
//align with side_ch_control.v and all related user space, remote files
#define CSI_LEN 56 // length of single CSI
#define EQUALIZER_LEN (56-4) // for non HT, four {32767,32767} will be padded to achieve 52 (non HT should have 48)
#define HEADER_LEN 2 //timestamp and frequency offset
#define MAX_NUM_DMA_SYMBOL 8192 //align with side_ch.v side_ch.h
#define SIDE_CH_REG_MULTI_RST_ADDR (0*4)
#define SIDE_CH_REG_CONFIG_ADDR (1*4)
#define SIDE_CH_REG_NUM_DMA_SYMBOL_ADDR (2*4) //low 16bit to PS; high 16bit to PL
#define SIDE_CH_REG_IQ_CAPTURE_ADDR (3*4)
#define SIDE_CH_REG_NUM_EQ_ADDR (4*4)
#define SIDE_CH_REG_FC_TARGET_ADDR (5*4)
#define SIDE_CH_REG_ADDR1_TARGET_ADDR (6*4)
#define SIDE_CH_REG_ADDR2_TARGET_ADDR (7*4)
#define SIDE_CH_REG_IQ_TRIGGER_ADDR (8*4)
#define SIDE_CH_REG_RSSI_TH_ADDR (9*4)
#define SIDE_CH_REG_GAIN_TH_ADDR (10*4)
#define SIDE_CH_REG_PRE_TRIGGER_LEN_ADDR (11*4)
#define SIDE_CH_REG_IQ_LEN_ADDR (12*4)
#define SIDE_CH_REG_M_AXIS_DATA_COUNT_ADDR (20*4)

View File

@ -283,8 +283,8 @@ static inline u32 hw_init(enum tx_intf_mode mode, u32 num_dma_symbol_to_pl, u32
tx_intf_api->TX_INTF_REG_NUM_DMA_SYMBOL_TO_PS_write(num_dma_symbol_to_ps);
tx_intf_api->TX_INTF_REG_CFG_DATA_TO_ANT_write(0);
tx_intf_api->TX_INTF_REG_TX_HOLD_THRESHOLD_write(420);
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x4F); //.src_sel0(slv_reg14[2:0]), .src_sel1(slv_reg14[6:4]), 0-s00_axis_tlast,1-ap_start,2-tx_start_from_acc,3-tx_end_from_acc,4-xpu signal
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x3004F); //disable interrupt
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x4); //.src_sel(slv_reg14[2:0]), 0-s00_axis_tlast,1-ap_start,2-tx_start_from_acc,3-tx_end_from_acc,4-tx_try_complete from xpu
tx_intf_api->TX_INTF_REG_INTERRUPT_SEL_write(0x30004); //disable interrupt
tx_intf_api->TX_INTF_REG_BB_GAIN_write(100);
tx_intf_api->TX_INTF_REG_ANT_SEL_write(ant_sel);
tx_intf_api->TX_INTF_REG_WIFI_TX_MODE_write((1<<3)|(2<<4));

View File

@ -713,12 +713,10 @@
sdr: sdr {
compatible ="sdr,sdr";
dmas = <&rx_dma 0
&rx_dma 1
&tx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "rx_dma_s2mm", "tx_dma_mm2s", "tx_dma_s2mm";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt0", "tx_itrpt1";
dmas = <&rx_dma 1
&tx_dma 0>;
dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 29 1 0 30 1 0 33 1 0 34 1>;
} ;
@ -788,20 +786,20 @@
};
tx_intf_0: tx_intf@83c00000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "s00_axis_aclk";//, "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>, <0x2 0x11>;
compatible = "sdr,tx_intf";
interrupt-names = "tx_itrpt0", "tx_itrpt1";
interrupt-names = "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 33 1 0 34 1>;
interrupts = <0 34 1>;
reg = <0x83c00000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
rx_intf_0: rx_intf@83c20000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "m00_axis_aclk";//, "s00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>;
compatible = "sdr,rx_intf";
interrupt-names = "not_valid_anymore", "rx_pkt_intr";
interrupt-parent = <1>;
@ -832,6 +830,16 @@
reg = <0x83c40000 0x10000>;
};
side_ch_0: side_ch@83c50000 {
clock-names = "s00_axi_aclk";
clocks = <0x2 0x11>;
compatible = "sdr,side_ch";
reg = <0x83c50000 0x10000>;
dmas = <&rx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "tx_dma_s2mm";
};
cf-ad9361-lpc@79020000 {
compatible = "adi,axi-ad9361-6.00.a";
reg = <0x79020000 0x6000>;

View File

@ -702,12 +702,10 @@
sdr: sdr {
compatible ="sdr,sdr";
dmas = <&rx_dma 0
&rx_dma 1
&tx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "rx_dma_s2mm", "tx_dma_mm2s", "tx_dma_s2mm";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt0", "tx_itrpt1";
dmas = <&rx_dma 1
&tx_dma 0>;
dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 29 1 0 30 1 0 33 1 0 34 1>;
} ;
@ -777,20 +775,20 @@
};
tx_intf_0: tx_intf@83c00000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "s00_axis_aclk";//, "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>, <0x2 0x11>;
compatible = "sdr,tx_intf";
interrupt-names = "tx_itrpt0", "tx_itrpt1";
interrupt-names = "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 33 1 0 34 1>;
interrupts = <0 34 1>;
reg = <0x83c00000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
rx_intf_0: rx_intf@83c20000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "m00_axis_aclk";//, "s00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>;
compatible = "sdr,rx_intf";
interrupt-names = "not_valid_anymore", "rx_pkt_intr";
interrupt-parent = <1>;
@ -821,6 +819,16 @@
reg = <0x83c40000 0x10000>;
};
side_ch_0: side_ch@83c50000 {
clock-names = "s00_axi_aclk";
clocks = <0x2 0x11>;
compatible = "sdr,side_ch";
reg = <0x83c50000 0x10000>;
dmas = <&rx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "tx_dma_s2mm";
};
cf-ad9361-lpc@79020000 {
compatible = "adi,axi-ad9361-6.00.a";
reg = <0x79020000 0x6000>;

View File

@ -922,12 +922,10 @@
sdr: sdr {
compatible ="sdr,sdr";
dmas = <&rx_dma 0
&rx_dma 1
&tx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "rx_dma_s2mm", "tx_dma_mm2s", "tx_dma_s2mm";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt0", "tx_itrpt1";
dmas = <&rx_dma 1
&tx_dma 0>;
dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 29 1 0 30 1 0 33 1 0 34 1>;
} ;
@ -997,20 +995,20 @@
};
tx_intf_0: tx_intf@83c00000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "s00_axis_aclk";//, "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>, <0x2 0x11>;
compatible = "sdr,tx_intf";
interrupt-names = "tx_itrpt0", "tx_itrpt1";
interrupt-names = "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 33 1 0 34 1>;
interrupts = <0 34 1>;
reg = <0x83c00000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
rx_intf_0: rx_intf@83c20000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "m00_axis_aclk";//, "s00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>;
compatible = "sdr,rx_intf";
interrupt-names = "not_valid_anymore", "rx_pkt_intr";
interrupt-parent = <1>;
@ -1041,6 +1039,16 @@
reg = <0x83c40000 0x10000>;
};
side_ch_0: side_ch@83c50000 {
clock-names = "s00_axi_aclk";
clocks = <0x2 0x11>;
compatible = "sdr,side_ch";
reg = <0x83c50000 0x10000>;
dmas = <&rx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "tx_dma_s2mm";
};
cf-ad9361-lpc@79020000 {
compatible = "adi,axi-ad9361-6.00.a";
reg = <0x79020000 0x6000>;

View File

@ -918,12 +918,10 @@
sdr: sdr {
compatible ="sdr,sdr";
dmas = <&rx_dma 0
&rx_dma 1
&tx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "rx_dma_s2mm", "tx_dma_mm2s", "tx_dma_s2mm";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt0", "tx_itrpt1";
dmas = <&rx_dma 1
&tx_dma 0>;
dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 29 1 0 30 1 0 33 1 0 34 1>;
} ;
@ -993,20 +991,20 @@
};
tx_intf_0: tx_intf@83c00000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "s00_axis_aclk";//, "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>, <0x2 0x11>;
compatible = "sdr,tx_intf";
interrupt-names = "tx_itrpt0", "tx_itrpt1";
interrupt-names = "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 33 1 0 34 1>;
interrupts = <0 34 1>;
reg = <0x83c00000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
rx_intf_0: rx_intf@83c20000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "m00_axis_aclk";//, "s00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>;
compatible = "sdr,rx_intf";
interrupt-names = "not_valid_anymore", "rx_pkt_intr";
interrupt-parent = <1>;
@ -1037,6 +1035,16 @@
reg = <0x83c40000 0x10000>;
};
side_ch_0: side_ch@83c50000 {
clock-names = "s00_axi_aclk";
clocks = <0x2 0x11>;
compatible = "sdr,side_ch";
reg = <0x83c50000 0x10000>;
dmas = <&rx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "tx_dma_s2mm";
};
cf-ad9361-lpc@79020000 {
compatible = "adi,axi-ad9361-6.00.a";
reg = <0x79020000 0x6000>;

View File

@ -2438,12 +2438,10 @@
sdr: sdr {
compatible ="sdr,sdr";
dmas = <&rx_dma 0
&rx_dma 1
&tx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "rx_dma_s2mm", "tx_dma_mm2s", "tx_dma_s2mm";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt0", "tx_itrpt1";
dmas = <&rx_dma 1
&tx_dma 0>;
dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt_useless", "tx_itrpt";
interrupts = <0 89 1 0 90 1 0 93 1 0 94 1>;
} ;
@ -2510,19 +2508,19 @@
};
tx_intf_0: tx_intf@a0005000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x3 0x49>, <0x3 0x49>, <0x3 0x49>, <0x3 0x49>;
clock-names = "s00_axi_aclk", "s00_axis_aclk";//, "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x3 0x49>, <0x3 0x49>;//, <0x3 0x49>, <0x3 0x49>;
compatible = "sdr,tx_intf";
interrupt-names = "tx_itrpt0", "tx_itrpt1";
interrupts = <0 93 1 0 94 1>;
interrupt-names = "tx_itrpt";
interrupts = <0 94 1>;
reg = <0xA0005000 0x1000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
rx_intf_0: rx_intf@a0004000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "m00_axis_aclk";
clocks = <0x3 0x49>, <0x3 0x49>, <0x3 0x49>;
clock-names = "s00_axi_aclk", "m00_axis_aclk";//, "s00_axis_aclk";
clocks = <0x3 0x49>, <0x3 0x49>;//, <0x3 0x49>;
compatible = "sdr,rx_intf";
interrupt-names = "not_valid_anymore", "rx_pkt_intr";
interrupts = <0 89 1 0 90 1>;
@ -2552,6 +2550,16 @@
reg = <0xA0006000 0x1000>;
};
side_ch_0: side_ch@a0007000 {
clock-names = "s00_axi_aclk";
clocks = <0x3 0x49>;
compatible = "sdr,side_ch";
reg = <0xA0007000 0x1000>;
dmas = <&rx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "tx_dma_s2mm";
};
cf-ad9361-lpc@99020000 {
compatible = "adi,axi-ad9361-6.00.a";
reg = <0x99020000 0x6000>;

View File

@ -623,7 +623,7 @@
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges;
/*
i2c@41600000 {
compatible = "xlnx,axi-iic-1.01.b", "xlnx,xps-iic-2.00.a";
reg = <0x41600000 0x10000>;
@ -679,7 +679,6 @@
};
};
/*
dma@43000000 {
compatible = "adi,axi-dmac-1.00.a";
reg = <0x43000000 0x10000>;
@ -740,7 +739,6 @@
linux,phandle = <0x13>;
phandle = <0x13>;
};
*/
axi-i2s@77600000 {
compatible = "adi,axi-i2s-1.00.a";
@ -754,7 +752,7 @@
phandle = <0x15>;
};
/*axi-sysid-0@45000000 {
axi-sysid-0@45000000 {
compatible = "adi,axi-sysid-1.00.a";
reg = <0x45000000 0x10000>;
};*/
@ -828,12 +826,10 @@
sdr: sdr {
compatible ="sdr,sdr";
dmas = <&rx_dma 0
&rx_dma 1
&tx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "rx_dma_s2mm", "tx_dma_mm2s", "tx_dma_s2mm";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt0", "tx_itrpt1";
dmas = <&rx_dma 1
&tx_dma 0>;
dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 29 1 0 30 1 0 33 1 0 34 1>;
} ;
@ -903,20 +899,20 @@
};
tx_intf_0: tx_intf@83c00000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "s00_axis_aclk";//, "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>, <0x2 0x11>;
compatible = "sdr,tx_intf";
interrupt-names = "tx_itrpt0", "tx_itrpt1";
interrupt-names = "tx_itrpt";
interrupt-parent = <1>;
interrupts = <0 33 1 0 34 1>;
interrupts = <0 34 1>;
reg = <0x83c00000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
rx_intf_0: rx_intf@83c20000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk", "m00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>, <0x2 0x11>;
clock-names = "s00_axi_aclk", "m00_axis_aclk";//, "s00_axis_aclk";
clocks = <0x2 0x11>, <0x2 0x11>;//, <0x2 0x11>;
compatible = "sdr,rx_intf";
interrupt-names = "not_valid_anymore", "rx_pkt_intr";
interrupt-parent = <1>;
@ -947,6 +943,16 @@
reg = <0x83c40000 0x10000>;
};
side_ch_0: side_ch@83c50000 {
clock-names = "s00_axi_aclk";
clocks = <0x2 0x11>;
compatible = "sdr,side_ch";
reg = <0x83c50000 0x10000>;
dmas = <&rx_dma 0
&tx_dma 1>;
dma-names = "rx_dma_mm2s", "tx_dma_s2mm";
};
cf-ad9361-lpc@79020000 {
compatible = "adi,axi-ad9361-6.00.a";
reg = <0x79020000 0x6000>;

@ -1 +1 @@
Subproject commit 98601d9cf69ffaaef3601d6eefdd6ab252c7e563
Subproject commit b3bd6e298feaa75f7be688f5fe2ded842351fca1

View File

@ -32,6 +32,9 @@ sudo apt-get -y install libnl-genl-3-dev
cd sdrctl_src
make
cp sdrctl ../
cd ../side_ch_ctl_src/
gcc -o side_ch_ctl side_ch_ctl.c
cp side_ch_ctl ../
cd ..
# install and setup dhcp server

View File

@ -1,5 +1,5 @@
//---nl80211 cmd testmode definitions
//---should be used in driver sdr.c and user space app like sdrctl, iw
//---should be used in driver sdr.c and user space app
enum openwifi_testmode_attr {
__OPENWIFI_ATTR_INVALID = 0,

View File

@ -0,0 +1,122 @@
#
# openwifi side info receive and display program
# Xianjun jiao. putaoshu@msn.com; xianjun.jiao@imec.be
#
import os
import sys
import socket
import numpy as np
import matplotlib.pyplot as plt
def display_iq(iq_capture, agc_gain, rssi_half_db):
fig_iq_capture = plt.figure(0)
fig_iq_capture.clf()
plt.xlabel("sample")
plt.ylabel("I/Q")
plt.title("I (blue) and Q (red) capture")
plt.plot(iq_capture.real, 'b')
plt.plot(iq_capture.imag, 'r')
plt.ylim(-32767, 32767)
fig_iq_capture.show()
plt.pause(0.0001)
agc_gain_lock = np.copy(agc_gain)
agc_gain_lock[agc_gain>127] = 80 # agc lock
agc_gain_lock[agc_gain<=127] = 0 # agc not lock
agc_gain_value = np.copy(agc_gain)
agc_gain_value[agc_gain>127] = agc_gain[agc_gain>127] - 128
fig_agc_gain = plt.figure(1)
fig_agc_gain.clf()
plt.xlabel("sample")
plt.ylabel("gain/lock")
plt.title("AGC gain (blue) and lock status (red)")
plt.plot(agc_gain_value, 'b')
plt.plot(agc_gain_lock, 'r')
plt.ylim(0, 82)
fig_agc_gain.show()
plt.pause(0.0001)
fig_rssi_half_db = plt.figure(2)
fig_rssi_half_db.clf()
plt.xlabel("sample")
plt.ylabel("dB")
plt.title("RSSI half dB (uncalibrated)")
plt.plot(rssi_half_db)
plt.ylim(100, 270)
fig_rssi_half_db.show()
plt.pause(0.0001)
def parse_iq(iq, iq_len):
# print(len(iq), iq_len)
num_dma_symbol_per_trans = 1 + iq_len
num_int16_per_trans = num_dma_symbol_per_trans*4 # 64bit per dma symbol
num_trans = round(len(iq)/num_int16_per_trans)
# print(len(iq), iq.dtype, num_trans)
iq = iq.reshape([num_trans, num_int16_per_trans])
timestamp = iq[:,0] + pow(2,16)*iq[:,1] + pow(2,32)*iq[:,2] + pow(2,48)*iq[:,3]
iq_capture = iq[:,4::4] + iq[:,5::4]*1j
agc_gain = iq[:,6::4]
rssi_half_db = iq[:,7::4]
# print(num_trans, iq_len, iq_capture.shape, agc_gain.shape, rssi_half_db.shape)
iq_capture = iq_capture.reshape([num_trans*iq_len,])
agc_gain = agc_gain.reshape([num_trans*iq_len,])
rssi_half_db = rssi_half_db.reshape([num_trans*iq_len,])
return timestamp, iq_capture, agc_gain, rssi_half_db
UDP_IP = "192.168.10.1" #Local IP to listen
UDP_PORT = 4000 #Local port to listen
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
# align with side_ch_control.v and all related user space, remote files
MAX_NUM_DMA_SYMBOL = 8192
if len(sys.argv)<2:
print("Assume iq_len = 8187! (Max UDP 65507 bytes; (65507/8)-1 = 8187)")
iq_len = 8187
else:
iq_len = int(sys.argv[1])
print(iq_len)
# print(type(num_eq))
if iq_len>8187:
iq_len = 8187
print('Limit iq_len to 8187! (Max UDP 65507 bytes; (65507/8)-1 = 8187)')
num_dma_symbol_per_trans = 1 + iq_len
num_byte_per_trans = 8*num_dma_symbol_per_trans
if os.path.exists("iq.txt"):
os.remove("iq.txt")
iq_fd=open('iq.txt','a')
while True:
try:
data, addr = sock.recvfrom(MAX_NUM_DMA_SYMBOL*8) # buffer size
# print(addr)
test_residual = len(data)%num_byte_per_trans
# print(len(data)/8, num_dma_symbol_per_trans, test_residual)
if (test_residual != 0):
print("Abnormal length")
iq = np.frombuffer(data, dtype='int16')
np.savetxt(iq_fd, iq)
timestamp, iq_capture, agc_gain, rssi_half_db = parse_iq(iq, iq_len)
print(timestamp)
display_iq(iq_capture, agc_gain, rssi_half_db)
except KeyboardInterrupt:
print('User quit')
break
print('close()')
side_info_fd.close()
sock.close()

View File

@ -0,0 +1,403 @@
/*
* openwifi side channel user space program
* Xianjun jiao. putaoshu@msn.com; xianjun.jiao@imec.be
*/
#include <sys/socket.h>
#include <linux/netlink.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
// #define NETLINK_USER 31
#define MAX_NUM_DMA_SYMBOL 8192 //align with side_ch.v side_ch.h
#define MAX_PAYLOAD (8*MAX_NUM_DMA_SYMBOL) /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
//align with side_ch_control.v and all related user space, remote files
#define CSI_LEN 56 // length of single CSI
#define EQUALIZER_LEN (56-4) // for non HT, four {32767,32767} will be padded to achieve 52 (non HT should have 48)
#define HEADER_LEN 2 //timestamp and frequency offset
#define ACTION_INVALID 0
#define ACTION_REG_WRITE 1
#define ACTION_REG_READ 2
#define ACTION_SIDE_INFO_GET 3
#define REG_TYPE_INVALID 0
#define REG_TYPE_HARDWARE 1
#define REG_TYPE_SOFTWARE 2
#define MAX_PARA_STRING_LEN 31
char tmp_str[MAX_PARA_STRING_LEN+1];
int take_reg_idx_string_for_write(char *para) { // return into tmp_str till 'd' 'D' 'h' 'H' or 0
int i = 0;
// while (para[i] != 'd' && para[i] != 'D' && para[i] != 'h' && para[i] != 'H' && para[i] != 0) {
while (para[i] != 'd' && para[i] != 'h' && para[i] != 0) {
tmp_str[i] = para[i];
i++;
}
if (i==0)
return(-1);
if (para[i-1] == 0) // we expect d D h H, not 0!
return(-2);
tmp_str[i] = 0;
return(i);
}
int take_reg_val_string_for_write(char *para) {
int i = 0;
while (para[i] != 0) {
tmp_str[i] = para[i];
i++;
}
if (i==0)
return(-1);
tmp_str[i] = 0;
return(i);
}
int all_zero_in_string(char *para) {
int i;
int check_len = strlen(para);
if (check_len == 0)
return(-1);
i = 0;
while (para[i] == '0')
i++;
if (i == check_len)
return(1);
return(0);
}
long int atoi_my(char *para) {
long int ret = all_zero_in_string(para);
if (ret<0)
return(-1);
if (ret==1)
return(0);
ret = atol(para);
if (ret==0)
return(-1);
return(ret);
}
long int hextoi_my(char *para) {
long int ret = all_zero_in_string(para);
if (ret<0)
return(-1);
if (ret==1)
return(0);
ret = strtoul(para, NULL, 16);
if (ret==0)
return(-1);
return(ret);
}
// parameter_string format:
// write 987 to hardware register 3: wh3d987 (w--write; h--hardware; 3 --register idx; d--decimal; 987--value)
// write 0x3db to software register 19: ws19h3db (w--write; s--software; 19--register idx; h--hex; 3db--value 0x3db)
// read software register 23: rs23 (r-- read; s--software; 23--register idx)
// get csi and equalizer output: g400 (g-- get; 400--every 400ms; no/wrong input means default 100ms)
int parse_para_string(char *para, int *action_flag, int *reg_type, int *reg_idx, unsigned int *reg_val, int *interval_ms) {
int i, para_string_len, num_char_reg_idx, num_char_reg_val, hex_flag;
para_string_len = strlen(para);
if (para_string_len == 0 || para_string_len>MAX_PARA_STRING_LEN) {
printf("Parameter string is too short/long!\n");
return(-1);
}
// process the csi/equalizer get command
if ( para[0] == 'g'){// || para[0] == 'G' ) {
(*action_flag) = ACTION_SIDE_INFO_GET;
if (para_string_len == 1) { // no explict input
(*interval_ms) = 100;
printf("The default 100ms side info getting period is taken!\n");
return(0);
}
// there is something input
(*interval_ms) = atoi_my(para+1);
if ( (*interval_ms)<0 ) { // for invalid input, we set it to the default 100ms
(*interval_ms) = 100;
printf("Invalid side info getting period!\n");
printf("The default 100ms side info getting period is taken!\n");
}
return(0);
}
if (para_string_len == 2) {// this is invalid, for read and write, the length should be > 2
printf("Lack of input (register index/value) for read/write action\n");
return(-2);
}
// process the register read command
if ( para[0] == 'r'){// || para[0] == 'R' ) {
(*action_flag) = ACTION_REG_READ;
if ( para[1] == 'h')// || para[1] == 'H' )
(*reg_type) = REG_TYPE_HARDWARE;
else if ( para[1] == 's')// || para[1] == 'S' )
(*reg_type) = REG_TYPE_SOFTWARE;
else {
(*reg_type) = REG_TYPE_INVALID;
printf("Invalid register type (s/h is expected)!\n");
return(-3);
}
(*reg_idx) = atoi_my(para+2);
if ( (*reg_idx)<0 || (*reg_idx)>31) {
printf("Invalid register index (should be 0~31)!\n");
return(-4);
}
return(0);
}
if (para_string_len < 5) { // this is invalid, for write, the length should be >= 5. example wh3d9
printf("Lack of input (register value/etc) for write action\n");
return(-5);
}
// process the register write command
if ( para[0] == 'w'){// || para[0] == 'W' ) {
(*action_flag) = ACTION_REG_WRITE;
if ( para[1] == 'h')// || para[1] == 'H' )
(*reg_type) = REG_TYPE_HARDWARE;
else if ( para[1] == 's')// || para[1] == 'S' )
(*reg_type) = REG_TYPE_SOFTWARE;
else {
(*reg_type) = REG_TYPE_INVALID;
printf("Invalid register type (s/h is expected)!\n");
return(-6);
}
num_char_reg_idx = take_reg_idx_string_for_write(para+2);
if ( num_char_reg_idx<0 ) {
printf("Invalid register index input!\n");
return(-7);
}
// if ((num_char_reg_idx+2)==para_string_len) //consume all string already
// return(-8);
(*reg_idx) = atoi_my(tmp_str);
if ( (*reg_idx)<0 || (*reg_idx)>31 ) {
printf("Invalid register index (should be 0~31)!\n");
return(-9);
}
if (para[2+num_char_reg_idx] == 'd')// || para[2+num_char_reg_idx] == 'D')
hex_flag=0;
else if (para[2+num_char_reg_idx] == 'h')// || para[2+num_char_reg_idx] == 'H')
hex_flag=1;
else {
printf("Invalid hex/decimal flag (d/h is expected)!\n");
return(-10);
}
num_char_reg_val = take_reg_val_string_for_write(para+2+num_char_reg_idx+1);
if ( num_char_reg_val<0 ) {
printf("Invalid register value input!\n");
return(-11);
}
if (hex_flag==0) {
(*reg_val) = atoi_my(tmp_str);
if ( (*reg_val)<0 ) {
printf("Invalid register value input of decimal number!\n");
return(-12);
}
} else {
(*reg_val) = hextoi_my(tmp_str);
// printf("%u %s\n", (*reg_val), tmp_str);
if ( (*reg_val)<0 ) {
printf("Invalid register value input of hex number!\n");
return(-13);
}
}
return(0);
}
return(-14);
}
void print_usage(void) {
printf("Usage: side_ch_ctl parameter_string\n");
printf("Example:\n");
printf("write 987 to hardware register 3: wh3d987 (w--write; h--hardware; 3 --register idx; d--decimal; 987--value)\n");
printf("write 0x3db to software register 19: ws19h3db (w--write; s--software; 19--register idx; h--hex; 3db--value 0x3db)\n");
printf(" read software register 23: rs23 (r-- read; s--software; 23--register idx)\n");
printf(" get csi and equalizer output: g400 (g-- get; 400--every 400ms; no/wrong input means default 100ms)\n");
}
volatile bool do_exit = false;
void sigint_callback_handler(int signum)
{
fprintf(stdout, "Caught signal %d\n", signum);
do_exit = true;
}
int main(const int argc, char * const argv[])
{
int action_flag, reg_type, reg_idx, interval_ms, s, side_info_size, socket_ok = 1, loop_count=0, side_info_count=0;
unsigned int reg_val, *cmd_buf;
unsigned short port;
struct sockaddr_in server;
int ret = 0;
if (argc!=2) {
printf("Wrong input!\n");
print_usage();
return(ret);
}
ret = parse_para_string(argv[1], &action_flag, &reg_type, &reg_idx, &reg_val, &interval_ms);
printf("parse: ret %d\n", ret);
printf(" tx: action_flag %d reg_type %d reg_idx %d reg_val %u interval_ms %d\n", action_flag, reg_type, reg_idx, reg_val, interval_ms);
if (ret<0) {
printf("Wrong input!\n");
print_usage();
return(ret);
}
// if (signal(SIGINT, &sigint_callback_handler)==SIG_ERR ||
// signal(SIGILL, &sigint_callback_handler)==SIG_ERR ||
// signal(SIGFPE, &sigint_callback_handler)==SIG_ERR ||
// signal(SIGSEGV, &sigint_callback_handler)==SIG_ERR ||
// signal(SIGTERM, &sigint_callback_handler)==SIG_ERR ||
// signal(SIGABRT, &sigint_callback_handler)==SIG_ERR) {
if (signal(SIGINT, &sigint_callback_handler)==SIG_ERR) {
printf("SIG_ERR!\n");
return(ret);
}
sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
if(sock_fd<0) {
printf("sock_fd %d\n", sock_fd);
return -1;
}
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
// memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
// nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
// strcpy(NLMSG_DATA(nlh), "Hello");
// udp socket setup
port = htons(4000); // port 4000 at remote server
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
printf("socket() error! Will not send info to remote.\n");
socket_ok = 0;
}
server.sin_family = AF_INET; /* Internet Domain */
server.sin_port = port; /* Server Port */
server.sin_addr.s_addr = inet_addr("192.168.10.1"); /* Server's Address */
while(do_exit==false) {
nlh->nlmsg_len = NLMSG_SPACE(4*4);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
cmd_buf = (unsigned int*)NLMSG_DATA(nlh);
cmd_buf[0] = action_flag;
cmd_buf[1] = reg_type;
cmd_buf[2] = reg_idx;
cmd_buf[3] = reg_val;
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// printf("Sending message to kernel\n");
sendmsg(sock_fd,&msg,0);
// printf("Waiting for message from kernel\n");
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
iov.iov_len = nlh->nlmsg_len;
/* Read message from kernel */
recvmsg(sock_fd, &msg, 0);
// printf("Received message payload: %s\n", (char *)NLMSG_DATA(nlh));
side_info_size = nlh->nlmsg_len-NLMSG_HDRLEN;
// printf("num_dma_symbol %d\n", side_info_size/8);
if (action_flag!=ACTION_SIDE_INFO_GET) {
printf(" rx: size %d val %d 0x%08x\n", side_info_size, cmd_buf[0], cmd_buf[0]);
break;
}
if (socket_ok && (side_info_size >= ((CSI_LEN+0*EQUALIZER_LEN+HEADER_LEN)*8)))
if (sendto(s, cmd_buf, side_info_size, 0, (struct sockaddr *)&server, sizeof(server)) < 0)
printf("sendto() error!\n");
side_info_count = side_info_count + (side_info_size>4);
loop_count++;
if ((loop_count%64) == 0)
printf("loop %d side info count %d\n", loop_count, side_info_count);
usleep(interval_ms*1000);
}
close(s);
close(sock_fd);
return(ret);
}

View File

@ -0,0 +1,162 @@
#
# openwifi side info receive and display program
# Xianjun jiao. putaoshu@msn.com; xianjun.jiao@imec.be
#
import os
import sys
import socket
import numpy as np
import matplotlib.pyplot as plt
def display_side_info(freq_offset, csi, equalizer, CSI_LEN, EQUALIZER_LEN):
if not hasattr(display_side_info, 'freq_offset_store'):
display_side_info.freq_offset_store = np.zeros((256,))
len_freq_offset = len(freq_offset)
display_side_info.freq_offset_store[:(256-len_freq_offset)] = display_side_info.freq_offset_store[len_freq_offset:]
display_side_info.freq_offset_store[(256-len_freq_offset):] = freq_offset
fig_freq_offset = plt.figure(0)
fig_freq_offset.clf()
plt.xlabel("packet idx")
plt.ylabel("Hz")
plt.title("freq offset")
plt.plot(display_side_info.freq_offset_store)
fig_freq_offset.show()
plt.pause(0.0001)
good_row_idx = 0
if ( len(equalizer)==0 ):
csi_for_plot = csi.T
else:
equalizer[equalizer == 32767+32767*1j] = 0
num_row_equalizer, num_col_equalizer = equalizer.shape
equalizer_for_plot = np.zeros((num_row_equalizer, num_col_equalizer)) + 1j*np.zeros((num_row_equalizer, num_col_equalizer))
num_row_csi, num_col_csi = csi.shape
csi_for_plot = np.zeros((num_row_csi, num_col_csi)) + 1j*np.zeros((num_row_csi, num_col_csi))
# only take out the good equalizer result, when output > 2000, it is not good
for i in range(num_row_equalizer):
if (not (np.any(abs(equalizer[i,:].real)>2000) or np.any(abs(equalizer[i,:].imag)>2000)) ):
equalizer_for_plot[good_row_idx,:] = equalizer[i,:]
csi_for_plot[good_row_idx,:] = csi[i,:]
good_row_idx = good_row_idx + 1
csi_for_plot = csi_for_plot[0:good_row_idx,:]
equalizer_for_plot = equalizer_for_plot[0:good_row_idx,:]
csi_for_plot = csi_for_plot.T
equalizer_for_plot = equalizer_for_plot.T
if ( (len(equalizer)==0) or ((len(equalizer)>0)and(good_row_idx>0)) ):
fig_csi = plt.figure(1)
fig_csi.clf()
ax_abs_csi = fig_csi.add_subplot(211)
ax_abs_csi.set_xlabel("subcarrier idx")
ax_abs_csi.set_ylabel("abs")
ax_abs_csi.set_title("CSI")
plt.plot(np.abs(csi_for_plot))
ax_phase_csi = fig_csi.add_subplot(212)
ax_phase_csi.set_xlabel("subcarrier idx")
ax_phase_csi.set_ylabel("phase")
plt.plot(np.angle(csi_for_plot))
fig_csi.show()
plt.pause(0.0001)
if ( (len(equalizer)>0) and (good_row_idx>0) ):
fig_equalizer = plt.figure(2)
fig_equalizer.clf()
plt.xlabel("I")
plt.ylabel("Q")
plt.title("equalizer")
plt.scatter(equalizer_for_plot.real, equalizer_for_plot.imag)
fig_freq_offset.show()
plt.pause(0.0001)
def parse_side_info(side_info, num_eq, CSI_LEN, EQUALIZER_LEN, HEADER_LEN):
# print(len(side_info), num_eq, CSI_LEN, EQUALIZER_LEN, HEADER_LEN)
CSI_LEN_HALF = round(CSI_LEN/2)
num_dma_symbol_per_trans = HEADER_LEN + CSI_LEN + num_eq*EQUALIZER_LEN
num_int16_per_trans = num_dma_symbol_per_trans*4 # 64bit per dma symbol
num_trans = round(len(side_info)/num_int16_per_trans)
# print(len(side_info), side_info.dtype, num_trans)
side_info = side_info.reshape([num_trans, num_int16_per_trans])
timestamp = side_info[:,0] + pow(2,16)*side_info[:,1] + pow(2,32)*side_info[:,2] + pow(2,48)*side_info[:,3]
freq_offset = (20e6*side_info[:,4]/512)/(2*3.14159265358979323846)
csi = np.zeros((num_trans, CSI_LEN), dtype='int16')
csi = csi + csi*1j
equalizer = np.zeros((0,0), dtype='int16')
if num_eq>0:
equalizer = np.zeros((num_trans, num_eq*EQUALIZER_LEN), dtype='int16')
equalizer = equalizer + equalizer*1j
for i in range(num_trans):
tmp_vec_i = side_info[i,8:(num_int16_per_trans-1):4]
tmp_vec_q = side_info[i,9:(num_int16_per_trans-1):4]
tmp_vec = tmp_vec_i + tmp_vec_q*1j
# csi[i,:] = tmp_vec[0:CSI_LEN]
csi[i,:CSI_LEN_HALF] = tmp_vec[CSI_LEN_HALF:CSI_LEN]
csi[i,CSI_LEN_HALF:] = tmp_vec[0:CSI_LEN_HALF]
if num_eq>0:
equalizer[i,:] = tmp_vec[CSI_LEN:(CSI_LEN+num_eq*EQUALIZER_LEN)]
# print(i, len(tmp_vec), len(tmp_vec[0:CSI_LEN]), len(tmp_vec[CSI_LEN:(CSI_LEN+num_eq*EQUALIZER_LEN)]))
return timestamp, freq_offset, csi, equalizer
UDP_IP = "192.168.10.1" #Local IP to listen
UDP_PORT = 4000 #Local port to listen
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
# align with side_ch_control.v and all related user space, remote files
MAX_NUM_DMA_SYMBOL = 8192
CSI_LEN = 56 # length of single CSI
EQUALIZER_LEN = (56-4) # for non HT, four {32767,32767} will be padded to achieve 52 (non HT should have 48)
HEADER_LEN = 2 # timestamp and frequency offset
if len(sys.argv)<2:
print("Assume num_eq = 8!")
num_eq = 8
else:
num_eq = int(sys.argv[1])
print(num_eq)
# print(type(num_eq))
num_dma_symbol_per_trans = HEADER_LEN + CSI_LEN + num_eq*EQUALIZER_LEN
num_byte_per_trans = 8*num_dma_symbol_per_trans
if os.path.exists("side_info.txt"):
os.remove("side_info.txt")
side_info_fd=open('side_info.txt','a')
while True:
try:
data, addr = sock.recvfrom(MAX_NUM_DMA_SYMBOL*8) # buffer size
# print(addr)
print(len(data), num_byte_per_trans)
test_residual = len(data)%num_byte_per_trans
if (test_residual != 0):
print("Abnormal length")
side_info = np.frombuffer(data, dtype='int16')
np.savetxt(side_info_fd, side_info)
timestamp, freq_offset, csi, equalizer = parse_side_info(side_info, num_eq, CSI_LEN, EQUALIZER_LEN, HEADER_LEN)
print(timestamp)
# print(freq_offset)
# print(csi[0,0:10])
# print(equalizer[0,0:10])
display_side_info(freq_offset, csi, equalizer, CSI_LEN, EQUALIZER_LEN)
except KeyboardInterrupt:
print('User quit')
break
print('close()')
side_info_fd.close()
sock.close()

View File

@ -0,0 +1,47 @@
% Xianjun Jiao. xianjun.jiao@imec.be; putaoshu@msn.com
clear all;
close all;
iq_len = 8187;
a = load('iq.txt');
len_a = floor(length(a)/4)*4;
a = a(1:len_a);
b = reshape(a, [4, length(a)/4])';
num_data_in_each_iq_capture = 1 + iq_len;
num_iq_capture = floor(size(b,1)/num_data_in_each_iq_capture);
iq_capture = zeros(iq_len, num_iq_capture);
timestamp = zeros(1, num_iq_capture);
agc_gain = zeros(iq_len, num_iq_capture);
rssi_half_db = zeros(iq_len, num_iq_capture);
for i=1:num_iq_capture
sp = (i-1)*num_data_in_each_iq_capture + 1;
ep = i*num_data_in_each_iq_capture;
timestamp(i) = b(sp,1) + (2^16)*b(sp,2) + (2^32)*b(sp,3) + (2^48)*b(sp,4);
iq_capture(:,i) = b((sp+1):ep,1) + 1i.*b((sp+1):ep,2);
agc_gain(:,i) = b((sp+1):ep,3);
rssi_half_db(:,i) = b((sp+1):ep,4);
end
iq_capture = iq_capture(:);
agc_gain = agc_gain(:);
rssi_half_db = rssi_half_db(:);
agc_gain_lock = zeros(iq_len*num_iq_capture,1);
agc_gain_lock(agc_gain>127) = 1;
agc_gain_value = agc_gain;
agc_gain_value(agc_gain_value>127) = agc_gain_value(agc_gain_value>127) - 128;
figure; plot(timestamp); title('time stamp (TSF value)'); ylabel('us'); xlabel('packet'); grid on;
figure; plot(rssi_half_db); title('RSSI half dB (uncalibrated)'); xlabel('sample'); ylabel('dB'); grid on;
figure;
plot(real(iq_capture)); hold on; plot(imag(iq_capture),'r'); title('I (blue) Q (red) sample'); xlabel('sample'); ylabel('I/Q'); grid on;
figure;
subplot(2,1,1); plot(agc_gain_lock); title('AGC lock status from AD9361'); xlabel('sample'); ylabel('status'); grid on;
subplot(2,1,2); plot(agc_gain_value); title('AGC gain from AD9361'); xlabel('sample'); ylabel('gain'); grid on;

View File

@ -0,0 +1,43 @@
% Xianjun Jiao. xianjun.jiao@imec.be; putaoshu@msn.com
clear all;
close all;
num_eq = 8;
a = load('side_info.txt');
len_a = floor(length(a)/4)*4;
a = a(1:len_a);
b = reshape(a, [4, length(a)/4])';
num_data_in_each_side_info = 2+56+num_eq*52;
num_side_info = floor(size(b,1)/num_data_in_each_side_info);
side_info = zeros(num_data_in_each_side_info, num_side_info);
timestamp = zeros(1, num_side_info);
freq_offset = zeros(1, num_side_info);
csi = zeros(56, num_side_info);
equalizer = zeros(num_eq*52, num_side_info);
for i=1:num_side_info
sp = (i-1)*num_data_in_each_side_info + 1;
ep = i*num_data_in_each_side_info;
timestamp(i) = b(sp,1) + (2^16)*b(sp,2) + (2^32)*b(sp,3) + (2^48)*b(sp,4);
freq_offset(i) = (20e6*b(sp+1,1)/512)/(2*pi);
side_info(:,i) = b(sp:ep,1) + 1i.*b(sp:ep,2);
csi(:,i) = side_info(3:58,i);
equalizer(:,i) = side_info(59:end,i);
end
csi = [csi(29:end,:); csi(1:28,:)];
equalizer = equalizer(:);
equalizer(equalizer == 32767+1i*32767) = NaN;
subplot(2,1,1); plot(abs(csi)); title('CSI'); ylabel('abs'); grid on;
subplot(2,1,2); plot(angle(csi)); ylabel('phase'); xlabel('subcarrier'); grid on;
if ~isempty(equalizer)
scatterplot(equalizer); grid on;
end
figure; plot(timestamp); title('time stamp (TSF value)'); ylabel('us'); xlabel('packet'); grid on;
figure; plot(freq_offset); title('freq offset (Hz)'); ylabel('Hz'); xlabel('packet'); grid on;

View File

@ -120,12 +120,14 @@ sudo wget -P $SDCARD_DIR/rootfs/root/openwifi/webserver/ https://users.ugent.be/
# build openwifi driver
$OPENWIFI_DIR/driver/make_all.sh $OPENWIFI_DIR $XILINX_DIR 32
$OPENWIFI_DIR/driver/side_ch/make_driver.sh $OPENWIFI_DIR $XILINX_DIR 32
# Copy files to SD card rootfs partition
sudo mkdir $SDCARD_DIR/rootfs/root/openwifi/drv32
sudo find $OPENWIFI_DIR/driver -name \*.ko -exec cp {} $SDCARD_DIR/rootfs/root/openwifi/drv32 \;
# build openwifi driver
$OPENWIFI_DIR/driver/make_all.sh $OPENWIFI_DIR $XILINX_DIR 64
$OPENWIFI_DIR/driver/side_ch/make_driver.sh $OPENWIFI_DIR $XILINX_DIR 64
# Copy files to SD card rootfs partition
sudo mkdir $SDCARD_DIR/rootfs/root/openwifi/drv64
sudo find $OPENWIFI_DIR/driver -name \*.ko -exec cp {} $SDCARD_DIR/rootfs/root/openwifi/drv64 \;