the side channel (timestamp, frequency offset, CSI, equalizer) feature

This commit is contained in:
Xianjun Jiao 2020-10-08 15:07:57 +02:00
parent 7da3f8ba0e
commit 22dd0cc486
30 changed files with 1567 additions and 92 deletions

View File

@ -25,6 +25,7 @@ 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 monitor (timestamp, frequency offset, channel response, equalizer) [[CSI notes](doc/app_notes/csi.md)]
- On roadmap: **802.11ax**
**Performance (AP: openwifi at channel 44, client: TL-WDN4200 N900 USB Dongle):**
@ -35,12 +36,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-5-32bit.img.xz)
zed_fmcs2|Xilinx zed board + FMCOMMS2/3/4|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-5-32bit.img.xz)
adrv9364z7020|ADRV9364-Z7020 + ADRV1CRR-BOB|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-5-32bit.img.xz)
adrv9361z7035|ADRV9361-Z7035 + ADRV1CRR-BOB/FMC|Done|[32bit img](https://users.ugent.be/~xjiao/openwifi-1.1.0-taiyuan-5-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-5-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-5-64bit.img.xz)
zcu102_9371|Xilinx ZCU102 dev board + ADRV9371|Future|None
- board_name is used to identify FPGA design in openwifi-hw/boards/
@ -85,6 +86,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.
@ -244,7 +246,7 @@ Low latency for gaming and introduction [[youtube](https://youtu.be/Notn9X482LI)
## 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,4 @@ 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

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

@ -0,0 +1,110 @@
## 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.
## 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 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
./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 match:
./side_ch_ctl wh1h1001
Turn on addr2 (source address) match:
./side_ch_ctl wh1h4001
Turn on 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:
```
Specify the FC matching target:
./side_ch_ctl wh5hY
Specify the addr1 (target address) matching target:
./side_ch_ctl wh6hY
Specify the addr2 (source address) matching target:
./side_ch_ctl wh7hY
```
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*100ms
## Understand CSI feature
The CSI information is extracted via the openwifi **side channel** infrastructure. This figure explains the related module (and related source code file name) and how the information goes from the 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 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).
## Run CSI together with mode other than monitor
The CSI could run with not only monitor mode. When you run openwifi in AP-Client or ad-hoc mode, after your communication functionality is fully up, you can start from "**insmod side_ch.ko**" and "**./side_ch_ctl g**" on board as described in the quick start section 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.
Please read the python and Matlab script to extract CSI information per packet 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

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

@ -0,0 +1,613 @@
/*
* 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
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");
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_START_DMA_TO_PS_read(void){
return reg_read(SIDE_CH_REG_START_DMA_TO_PS_ADDR);
}
static inline void SIDE_CH_REG_START_DMA_TO_PS_write(u32 value){
reg_write(SIDE_CH_REG_START_DMA_TO_PS_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_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 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);
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);
printk(KERN_INFO "%s recv msg: get_side_info(%d) res %d\n", side_ch_compatible_str, num_eq_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----------------
//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);
printk("%s dev_probe: num_eq_init %d\n",side_ch_compatible_str, num_eq_init);
// SIDE_CH_REG_CONFIG_write(0X6001); // match addr1 and addr2; bit12 FC; bit13 addr1; bit14 addr2
SIDE_CH_REG_CONFIG_write(0x0001); // match all packets by default; bit12 FC; bit13 addr1; bit14 addr2
SIDE_CH_REG_NUM_EQ_write(num_eq_init); // capture CSI + 8*equalizer by default
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");

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

@ -0,0 +1,22 @@
// 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 4096 //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_START_DMA_TO_PS_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_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

@ -828,12 +828,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 +901,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 +945,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

@ -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,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 4096 //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: g4 (g-- get; 4--every 4*100ms; 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_100ms) {
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_100ms) = 1;
printf("The default 1*100ms side info getting period is taken!\n");
return(0);
}
// there is something input
(*interval_100ms) = atoi_my(para+1);
if ( (*interval_100ms)<0 ) { // for invalid input, we set it to the default 100ms = 1*100ms;
(*interval_100ms) = 1;
printf("Invalid side info getting period!\n");
printf("The default 1*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: g2 (g-- get; 4--every 4*100ms; 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_100ms, 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_100ms);
printf("parse: ret %d\n", ret);
printf(" tx: action_flag %d reg_type %d reg_idx %d reg_val %u interval_100ms %d\n", action_flag, reg_type, reg_idx, reg_val, interval_100ms);
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("%d %d %d %d %d %d %d %d\n", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3], cmd_buf[4], cmd_buf[5], cmd_buf[6], cmd_buf[7]);
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_100ms*100*1000);
}
close(s);
close(sock_fd);
return(ret);
}

View File

@ -0,0 +1,161 @@
#
# 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
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(32768) # 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,40 @@
% Xianjun Jiao. xianjun.jiao@imec.be; putaoshu@msn.com
clear all;
close all;
num_eq = 8;
a = load('side_info.txt');
b = reshape(a, [4, length(a)/4])';
num_data_in_each_side_info = 2+56+num_eq*52;
num_side_info = 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 \;