python3
sudo apt-get install python3-picamera
https://www.raspberrypi.org/documentation/usage/camera/python/README.md
allow cv2.VideoCapture(0)
sudo modprobe bcm2835-v4l2
allow user access camera
sudo usermod XXXX -a -G video
single image display
import cv2
im = cv2.imread('test.jpg')
cv2.imshow('image', im)
cv2.waitKey()
video display
import cv2
ca = cv2.VideoCapture(0)
cv2.startWindowThread()
cv2.namedWindow('preview')
while True:
_, im = ca.read()
cv2.imshow('preview', im)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
Tuesday, February 28, 2017
Monday, February 27, 2017
open cv 3 on ubuntu
sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
sudo apt-get update
cd opencv-3.2.0/
mkdir build
cd build/
sudo apt-get install python3-dev
sudo apt-get install java-7-openjdk
sudo pip3 install --upgrade pip
sudo pip3 install numpy
export PYTHON3_EXECUTABLE=/usr/bin/python3
export PYTHON_INCLUDE_DIR=/usr/include/python3
export PYTHON_INCLUDE_DIR=/usr/include/python3.4
export PYTHON_INCLUDE_DIR2=/usr/include/x86_64-linux-gnu/python3.4m/
export PYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.4m.so
export PYTHON3_NUMPY_INCLUDE_DIRS=/usr/local/lib/python3.4/dist-packages/numpy/core/include/
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
sudo make -j4
sudo make install
For raspberry pi 3:
sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python3-dev python3-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
export PYTHON3_EXECUTABLE=/usr/bin/python3
export PYTHON_INCLUDE_DIR=/usr/include/python3.4
export PYTHON_INCLUDE_DIR2=/usr/include/arm-linux-gnueabihf/python3.4m/
export PYTHON_LIBRARY=/usr/lib/arm-linux-gnueabihf/libpython3.4m.so
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_QT=OFF -D WITH_TBB=OFF ..
http://stackoverflow.com/questions/26371187/how-can-i-disable-opengl-support-opencv
Sunday, February 26, 2017
tensorflow cifar10
https://www.tensorflow.org/tutorials/deep_cnn
First time the training is very slow.
On GTX 970, ~33 hours.
On GTX 860m, ~40 hours.
On Raspberry pi 3, ~200 hours.
I decided to use GTX 860m to train and will update back after 40 hours. (20170226, 9AM)
Or I am thinking of using multiple GTX860m on multiple PCs. I have 3 of them.
https://www.tensorflow.org/deploy/distributed
cifar10_image.py: This is a short script displaying 32x32 images from cifar10 bin file
import numpy as np
import PIL.Image
#display larger images
def DisplayPicture(byte, fmt='jpeg'):
print(byte[0])
a = np.fromstring(byte[1:3073], dtype=np.uint8)
b = np.reshape(a, (3, 1024))
c = b.T
d = np.concatenate([c, c, c, c, c, c, c, c], axis=1)
e = np.reshape(d, [32, 32*3*8])
f = np.concatenate([e, e, e, e, e, e, e, e], axis=1)
g = np.reshape(f, [1, 3072*8*8])
im = PIL.Image.frombytes('RGB', (32*8,32*8), g)
im.show()
#open file
#read an image
#record format: 1 byte type, 1024 byte red, 1024 byte green, 1024 byte blue
#32 x 32, row-major
f = open('data_batch_1.bin', 'rb')
byte = f.read(3073)
while byte != '':
DisplayPicture(byte)
byte = f.read(3073)
f.close
Further reading:
https://www.cs.toronto.edu/~kriz/cifar.html
http://groups.csail.mit.edu/vision/TinyImages/
First time the training is very slow.
On GTX 970, ~33 hours.
On GTX 860m, ~40 hours.
On Raspberry pi 3, ~200 hours.
I decided to use GTX 860m to train and will update back after 40 hours. (20170226, 9AM)
Or I am thinking of using multiple GTX860m on multiple PCs. I have 3 of them.
https://www.tensorflow.org/deploy/distributed
cifar10_image.py: This is a short script displaying 32x32 images from cifar10 bin file
import numpy as np
import PIL.Image
#display larger images
def DisplayPicture(byte, fmt='jpeg'):
print(byte[0])
a = np.fromstring(byte[1:3073], dtype=np.uint8)
b = np.reshape(a, (3, 1024))
c = b.T
d = np.concatenate([c, c, c, c, c, c, c, c], axis=1)
e = np.reshape(d, [32, 32*3*8])
f = np.concatenate([e, e, e, e, e, e, e, e], axis=1)
g = np.reshape(f, [1, 3072*8*8])
im = PIL.Image.frombytes('RGB', (32*8,32*8), g)
im.show()
#read an image
#record format: 1 byte type, 1024 byte red, 1024 byte green, 1024 byte blue
#32 x 32, row-major
f = open('data_batch_1.bin', 'rb')
byte = f.read(3073)
while byte != '':
DisplayPicture(byte)
byte = f.read(3073)
f.close
Further reading:
https://www.cs.toronto.edu/~kriz/cifar.html
http://groups.csail.mit.edu/vision/TinyImages/
Saturday, February 25, 2017
tensorflow Mandelbrot example
The last two lines of DisplayFractal() will show the picture. The https://www.tensorflow.org/tutorials/mandelbrot is not correct.
# Import libraries for simulation
import tensorflow as tf
import numpy as np
# Imports for visualization
import PIL.Image
from io import BytesIO
#from IPython.display import Image, display
def DisplayFractal(a, fmt='jpeg'):
"""Display an array of iteration counts as a
colorful picture of a fractal."""
a_cyclic = (6.28*a/20.0).reshape(list(a.shape)+[1])
img = np.concatenate([10+20*np.cos(a_cyclic),
30+50*np.sin(a_cyclic),
155-80*np.cos(a_cyclic)], 2)
img[a==a.max()] = 0
a = img
a = np.uint8(np.clip(a, 0, 255))
#f = BytesIO()
#PIL.Image.fromarray(a).save(f, fmt)
#display(Image(data=f.getvalue()))
im = PIL.Image.fromarray(a)
im.show()
sess = tf.InteractiveSession()
# Use NumPy to create a 2D array of complex numbers
Y, X = np.mgrid[-1.3:1.3:0.005, -2:1:0.005]
Z = X+1j*Y
xs = tf.constant(Z.astype(np.complex64))
zs = tf.Variable(xs)
ns = tf.Variable(tf.zeros_like(xs, tf.float32))
tf.global_variables_initializer().run()
# Compute the new values of z: z^2 + x
zs_ = zs*zs + xs
# Have we diverged with this new value?
not_diverged = tf.abs(zs_) < 4
# Operation to update the zs and the iteration count.
#
# Note: We keep computing zs after they diverge! This
# is very wasteful! There are better, if a little
# less simple, ways to do this.
#
step = tf.group(
zs.assign(zs_),
ns.assign_add(tf.cast(not_diverged, tf.float32))
)
for i in range(200): step.run()
#DisplayFractal(ns.eval())
# Import libraries for simulation
import tensorflow as tf
import numpy as np
# Imports for visualization
import PIL.Image
from io import BytesIO
#from IPython.display import Image, display
def DisplayFractal(a, fmt='jpeg'):
"""Display an array of iteration counts as a
colorful picture of a fractal."""
a_cyclic = (6.28*a/20.0).reshape(list(a.shape)+[1])
img = np.concatenate([10+20*np.cos(a_cyclic),
30+50*np.sin(a_cyclic),
155-80*np.cos(a_cyclic)], 2)
img[a==a.max()] = 0
a = img
a = np.uint8(np.clip(a, 0, 255))
#f = BytesIO()
#PIL.Image.fromarray(a).save(f, fmt)
#display(Image(data=f.getvalue()))
im = PIL.Image.fromarray(a)
im.show()
sess = tf.InteractiveSession()
# Use NumPy to create a 2D array of complex numbers
Y, X = np.mgrid[-1.3:1.3:0.005, -2:1:0.005]
Z = X+1j*Y
xs = tf.constant(Z.astype(np.complex64))
zs = tf.Variable(xs)
ns = tf.Variable(tf.zeros_like(xs, tf.float32))
tf.global_variables_initializer().run()
# Compute the new values of z: z^2 + x
zs_ = zs*zs + xs
# Have we diverged with this new value?
not_diverged = tf.abs(zs_) < 4
# Operation to update the zs and the iteration count.
#
# Note: We keep computing zs after they diverge! This
# is very wasteful! There are better, if a little
# less simple, ways to do this.
#
step = tf.group(
zs.assign(zs_),
ns.assign_add(tf.cast(not_diverged, tf.float32))
)
for i in range(200): step.run()
#DisplayFractal(ns.eval())
Friday, February 24, 2017
expand ubuntu partition on vmwave esxi 5
Original
admin@droneteambasevm:~$ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/UbntSR--vCAC--Tmp--vg-root 14048320 3345632 9966024 26% /
none 4 0 4 0% /sys/fs/cgroup
udev 3042888 4 3042884 1% /dev
tmpfs 610808 3036 607772 1% /run
none 5120 0 5120 0% /run/lock
none 3054024 0 3054024 0% /run/shm
none 102400 0 102400 0% /run/user
/dev/sda1 240972 38813 189718 17% /boot
admin@droneteambasevm:~$ sudo fdisk -l
[sudo] password for admin:
Disk /dev/sda: 107.4 GB, 107374182400 bytes
255 heads, 63 sectors/track, 13054 cylinders, total 209715200 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000a8393
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 499711 248832 83 Linux
/dev/sda2 501758 33552383 xxxxxxxx 5 Extended
/dev/sda5 501760 33552383 xxxxxxxx 83 Linux
Step 1:
sudo fdisk /dev/sda
d 5
d 2
n 2 (extended)
n 5 (logical 501760 to 33552383)
n 6 (logical reset)
t 5 8e
t 6 8e
p
This is results.
Command (m for help): p
Disk /dev/sda: 107.4 GB, 107374182400 bytes
255 heads, 63 sectors/track, 13054 cylinders, total 209715200 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000a8393
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 499711 248832 83 Linux
/dev/sda2 499712 209715199 104607744 5 Extended
/dev/sda5 501760 33552383 16525312 8e Linux LVM
/dev/sda6 33554432 209715199 88080384 83 Linux LVM
sudo reboot
Step 2:
admin@droneteambasevm:~$ sudo vgextend UbntSR-vCAC-Tmp-vg /dev/sda6
[sudo] password for admin:
No physical volume label read from /dev/sda6
Physical volume "/dev/sda6" successfully created
Volume group "UbntSR-vCAC-Tmp-vg" successfully extended
admin@droneteambasevm:~$ sudo lvextend -L +84G /dev/UbntSR-vCAC-Tmp-vg/root
Extending logical volume root to 97.74 GiB
Logical volume root successfully resized
admin@droneteambasevm:~$ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/UbntSR--vCAC--Tmp--vg-root 14048320 3347364 9964292 26% /
none 4 0 4 0% /sys/fs/cgroup
udev 3042888 4 3042884 1% /dev
tmpfs 610808 3040 607768 1% /run
none 5120 0 5120 0% /run/lock
none 3054024 0 3054024 0% /run/shm
none 102400 0 102400 0% /run/user
/dev/sda1 240972 38813 189718 17% /boot
admin@droneteambasevm:~$ sudo resize2fs /dev/UbntSR-vCAC-Tmp-vg/root
resize2fs 1.42.9 (4-Feb-2014)
Filesystem at /dev/UbntSR-vCAC-Tmp-vg/root is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 7
The filesystem on /dev/UbntSR-vCAC-Tmp-vg/root is now 25621504 blocks long.
admin@droneteambasevm:~$ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/UbntSR--vCAC--Tmp--vg-root 100746672 3364584 93122788 4% /
none 4 0 4 0% /sys/fs/cgroup
udev 3042888 4 3042884 1% /dev
tmpfs 610808 3040 607768 1% /run
none 5120 0 5120 0% /run/lock
none 3054024 0 3054024 0% /run/shm
none 102400 0 102400 0% /run/user
/dev/sda1 240972 38813 189718 17% /boot
admin@droneteambasevm:~$ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/UbntSR--vCAC--Tmp--vg-root 14048320 3345632 9966024 26% /
none 4 0 4 0% /sys/fs/cgroup
udev 3042888 4 3042884 1% /dev
tmpfs 610808 3036 607772 1% /run
none 5120 0 5120 0% /run/lock
none 3054024 0 3054024 0% /run/shm
none 102400 0 102400 0% /run/user
/dev/sda1 240972 38813 189718 17% /boot
admin@droneteambasevm:~$ sudo fdisk -l
[sudo] password for admin:
Disk /dev/sda: 107.4 GB, 107374182400 bytes
255 heads, 63 sectors/track, 13054 cylinders, total 209715200 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000a8393
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 499711 248832 83 Linux
/dev/sda2 501758 33552383 xxxxxxxx 5 Extended
/dev/sda5 501760 33552383 xxxxxxxx 83 Linux
Step 1:
sudo fdisk /dev/sda
d 5
d 2
n 2 (extended)
n 5 (logical 501760 to 33552383)
n 6 (logical reset)
t 5 8e
t 6 8e
p
This is results.
Command (m for help): p
Disk /dev/sda: 107.4 GB, 107374182400 bytes
255 heads, 63 sectors/track, 13054 cylinders, total 209715200 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000a8393
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 499711 248832 83 Linux
/dev/sda2 499712 209715199 104607744 5 Extended
/dev/sda5 501760 33552383 16525312 8e Linux LVM
/dev/sda6 33554432 209715199 88080384 83 Linux LVM
sudo reboot
Step 2:
admin@droneteambasevm:~$ sudo vgextend UbntSR-vCAC-Tmp-vg /dev/sda6
[sudo] password for admin:
No physical volume label read from /dev/sda6
Physical volume "/dev/sda6" successfully created
Volume group "UbntSR-vCAC-Tmp-vg" successfully extended
admin@droneteambasevm:~$ sudo lvextend -L +84G /dev/UbntSR-vCAC-Tmp-vg/root
Extending logical volume root to 97.74 GiB
Logical volume root successfully resized
admin@droneteambasevm:~$ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/UbntSR--vCAC--Tmp--vg-root 14048320 3347364 9964292 26% /
none 4 0 4 0% /sys/fs/cgroup
udev 3042888 4 3042884 1% /dev
tmpfs 610808 3040 607768 1% /run
none 5120 0 5120 0% /run/lock
none 3054024 0 3054024 0% /run/shm
none 102400 0 102400 0% /run/user
/dev/sda1 240972 38813 189718 17% /boot
admin@droneteambasevm:~$ sudo resize2fs /dev/UbntSR-vCAC-Tmp-vg/root
resize2fs 1.42.9 (4-Feb-2014)
Filesystem at /dev/UbntSR-vCAC-Tmp-vg/root is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 7
The filesystem on /dev/UbntSR-vCAC-Tmp-vg/root is now 25621504 blocks long.
admin@droneteambasevm:~$ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/UbntSR--vCAC--Tmp--vg-root 100746672 3364584 93122788 4% /
none 4 0 4 0% /sys/fs/cgroup
udev 3042888 4 3042884 1% /dev
tmpfs 610808 3040 607768 1% /run
none 5120 0 5120 0% /run/lock
none 3054024 0 3054024 0% /run/shm
none 102400 0 102400 0% /run/user
/dev/sda1 240972 38813 189718 17% /boot
Tuesday, February 21, 2017
tensorflow 1.0 on raspberry pi 3
Still ongoing.
Reference:
https://github.com/samjabrahams/tensorflow-on-raspberry-pi/blob/master/GUIDE.md
bazel:
/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
Add @ 600th line
} catch (InterruptedException e) {
throw new RepositoryFunctionException(
new IOException("thread interrupted"), Transience.TRANSIENT);
tensorflow:
git clone --recurse-submodules https://github.com/tensorflow/tensorflow
git checkout r1.0
cd tensorflow
...
/WORKSPACE
manually change the url line for numericjs_numeric_min_js to the following
url = "http://numericjs.com/numeric/lib/numeric-1.2.6.min.js",
Reference:
https://github.com/samjabrahams/tensorflow-on-raspberry-pi/blob/master/GUIDE.md
bazel:
/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
Add @ 600th line
} catch (InterruptedException e) {
throw new RepositoryFunctionException(
new IOException("thread interrupted"), Transience.TRANSIENT);
git clone --recurse-submodules https://github.com/tensorflow/tensorflow
git checkout r1.0
cd tensorflow
...
/WORKSPACE
manually change the url line for numericjs_numeric_min_js to the following
url = "http://numericjs.com/numeric/lib/numeric-1.2.6.min.js",
Then, ./configure
Monday, February 13, 2017
Atheros ath9k CSI Tools
https://github.com/xieyaxiongfly/Atheros-CSI-Tool
Kernel Version 4.1.10
Diff with the origin... Could it be ported?
diff -uNr linux-4.1.10/arch/sh/boot/compressed/vmlinux.scr Atheros-CSI-Tool/arch/sh/boot/compressed/vmlinux.scr
--- linux-4.1.10/arch/sh/boot/compressed/vmlinux.scr 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/arch/sh/boot/compressed/vmlinux.scr 1969-12-31 19:00:00.000000000 -0500
@@ -1,10 +0,0 @@
-SECTIONS
-{
- .rodata..compressed : {
- input_len = .;
- LONG(input_data_end - input_data) input_data = .;
- *(.data)
- output_len = . - 4;
- input_data_end = .;
- }
-}
diff -uNr linux-4.1.10/arch/sh/boot/romimage/vmlinux.scr Atheros-CSI-Tool/arch/sh/boot/romimage/vmlinux.scr
--- linux-4.1.10/arch/sh/boot/romimage/vmlinux.scr 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/arch/sh/boot/romimage/vmlinux.scr 1969-12-31 19:00:00.000000000 -0500
@@ -1,8 +0,0 @@
-SECTIONS
-{
- .text : {
- zero_page_pos = .;
- *(.data)
- end_data = .;
- }
-}
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.c 1969-12-31 19:00:00.000000000 -0500
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.c 2017-02-13 17:33:46.943579002 -0500
@@ -0,0 +1,325 @@
+/*
+ * =====================================================================================
+ * Filename: ath9k_csi.c
+ *
+ * Description: extrac csi and data together from hardware
+ * Version: 1.0
+ *
+ * Author: Yaxiong Xie
+ * Email : <xieyaxiongfly@gmail.com>
+ * Organization: WANDS group @ Nanyang Technological University
+ *
+ * Copyright (c) WANDS group @ Nanyang Technological University
+ * =====================================================================================
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/netdevice.h>
+
+#include "ar9003_csi.h"
+#include "ar9003_mac.h"
+#include "ar9003_phy.h"
+#include "mac.h"
+#include "hw.h"
+
+#define Tx_Buf_LEN 20480 // Generally, buffere with 20480 bits is enough
+ // You can change the size freely
+
+//#define Rx_Buf_LEN 128 // Generally, buffere with 20480 bits is enough
+
+#define BITS_PER_BYTE 8
+#define BITS_PER_SYMBOL 10
+#define BITS_PER_COMPLEX_SYMBOL (2 * BITS_PER_SYMBOL)
+
+#define DEVICE_NAME "CSI_dev"
+#define CLASS_NAME "CSI_class"
+#define AH_MAX_CHAINS 3 //maximum chain number, we set it to 3
+#define NUM_OF_CHAINMASK (1 << AH_MAX_CHAINS)
+
+
+volatile u32 csi_head;
+volatile u32 csi_tail;
+volatile u32 csi_len;
+volatile u32 csi_valid;
+volatile u32 recording;
+
+static struct ath9k_csi csi_buf[16];
+static char tx_buf[Tx_Buf_LEN];
+//static char rx_buf[Rx_Buf_LEN];
+
+static int majorNumber;
+static struct class* ebbcharClass = NULL;
+static struct device* ebbcharDevice = NULL;
+
+DECLARE_WAIT_QUEUE_HEAD(csi_queue);
+
+static int csi_open(struct inode *inode, struct file *file);
+static int csi_close(struct inode *inode, struct file *file);
+static ssize_t csi_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos);
+static ssize_t csi_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos);
+
+static const struct file_operations csi_fops = {
+ .read = csi_read,
+ .write = csi_write,
+ .open = csi_open,
+ .release = csi_close,
+ .llseek = default_llseek,
+};
+
+static u_int8_t Num_bits_on[NUM_OF_CHAINMASK] = {
+ 0 /* 000 */,
+ 1 /* 001 */,
+ 1 /* 010 */,
+ 2 /* 011 */,
+ 1 /* 100 */,
+ 2 /* 101 */,
+ 2 /* 110 */,
+ 3 /* 111 */
+};
+
+u_int8_t ar9300_get_nrx_csi(struct ath_hw *ah)
+{
+ return Num_bits_on[ah->rxchainmask];
+}
+
+static int __init csi_init(void)
+{
+
+ // initalize parameters
+ csi_head = 0;
+ csi_tail = 0;
+ recording = 0;
+ csi_valid = 0;
+
+ // Try to dynamically allocate a major number for the device -- more difficult but worth it
+ majorNumber = register_chrdev(0, DEVICE_NAME, &csi_fops);
+ if (majorNumber<0){
+ printk(KERN_ALERT "debug_csi: failed to register a major number\n");
+ return majorNumber;
+ }
+ printk(KERN_INFO "debug_csi: registered correctly with major number %d\n", majorNumber);
+
+ // Register the device class
+ ebbcharClass = class_create(THIS_MODULE, CLASS_NAME);
+ if (IS_ERR(ebbcharClass)){ // Check for error and clean up if there is
+ unregister_chrdev(majorNumber, DEVICE_NAME);
+ printk(KERN_ALERT "debug_csi: Failed to register device class\n");
+ return PTR_ERR(ebbcharClass); // Correct way to return an error on a pointer
+ }
+ printk(KERN_INFO "debug_csi: device class registered correctly\n");
+
+ // Register the device driver
+ ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
+ if (IS_ERR(ebbcharDevice)){ // Clean up if there is an error
+ class_destroy(ebbcharClass); // Repeated code but the alternative is goto statements
+ unregister_chrdev(majorNumber, DEVICE_NAME);
+ printk(KERN_ALERT "Failed to create the device\n");
+ return PTR_ERR(ebbcharDevice);
+ }
+ printk(KERN_INFO "debug_csi: device class created correctly \n"); // Made it! device was initialized
+
+ return 0;
+}
+
+static void __exit csi_exit(void)
+{
+ /* delete and unregister the devices we have created and registered */
+ device_destroy(ebbcharClass, MKDEV(majorNumber, 0)); // remove the device
+ class_unregister(ebbcharClass); // unregister the device class
+ class_destroy(ebbcharClass); // remove the device class
+ unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
+ printk(KERN_INFO "debug_csi: Goodbye CSI device!\n");
+
+}
+
+static int csi_open(struct inode *inode, struct file *file)
+{
+ printk(KERN_ALERT "debug_csi: csi open! \n");
+ recording = 1; // we can begin to record when
+ // the devices is open
+ return 0;
+}
+
+static int csi_close(struct inode *inode, struct file *file)
+{
+ printk(KERN_ALERT "debug_csi: csi close! \n");
+ recording = 0; // close and reset
+ return 0;
+}
+
+static ssize_t csi_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ u_int16_t len;
+ u_int8_t *csi_buf_addr;
+ u_int8_t *payload_buf_addr;
+ u_int16_t csi_len, payload_len;
+ struct ath9k_csi* csi;
+ struct csi_pkt_status *RxStatus;
+
+ *ppos = 0;
+
+ if (csi_head == csi_tail)
+ { // wait until time out
+ wait_event_interruptible_timeout(csi_queue,csi_head != csi_tail, 5*HZ);
+ }
+ if(csi_head != csi_tail){
+ csi = (struct ath9k_csi*)&csi_buf[csi_tail];
+ len = 0;
+
+ RxStatus = &(csi->pkt_status); // the status struct
+
+ csi_len = RxStatus->csi_len; // csi length (bytes)
+ csi_buf_addr = csi->csi_buf; // csi buffer
+ payload_len = csi->payload_len; // payload length (bytes)
+ payload_buf_addr = csi->payload_buf; // payload buffer
+
+
+ memcpy(tx_buf,RxStatus,23); // copy the status to the buffer
+ len += 23;
+
+ memcpy(tx_buf+len,&payload_len, 2); // record the length of payload
+ len += 2;
+
+ if (csi_len > 0){
+ memcpy(tx_buf+len,csi_buf_addr,csi_len); // copy csi to the buffer
+ len += csi_len;
+ }
+
+ memcpy(tx_buf+len,payload_buf_addr, payload_len); // copy payload to the buffer
+ len += payload_len;
+
+ memcpy(tx_buf+len,&len, 2); // record how many bytes we copy
+ len += 2;
+
+ copy_to_user(user_buf,tx_buf,len); // COPY
+
+ csi_tail = (csi_tail+1) & 0x0000000F; // delete the buffer
+ return len;
+ }else{
+ return 0;
+ }
+}
+
+static ssize_t csi_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ printk(KERN_ALERT "debug_csi: csi write!\n");
+ return 0;
+}
+
+void csi_record_payload(void* data, u_int16_t data_len)
+{
+ struct ath9k_csi* csi;
+ if(recording )
+ {
+ if( ((csi_head + 1) & 0x0000000F) == csi_tail) // check and update
+ csi_tail = (csi_tail + 1) & 0x0000000F;
+
+ csi = (struct ath9k_csi*)&csi_buf[csi_head];
+ memcpy((void*)(csi->payload_buf),data, data_len); // copy the payload
+ csi->payload_len = data_len; // record the payload length (bytes)
+ csi_valid = 1;
+ }
+}
+EXPORT_SYMBOL(csi_record_payload);
+
+void csi_record_status(struct ath_hw *ah, struct ath_rx_status *rxs, struct ar9003_rxs *rxsp,void* data)
+{
+ struct ath9k_csi* csi;
+
+ u_int8_t nr;
+ u_int8_t chan_BW;
+ u_int8_t rx_not_sounding;
+ u_int8_t rx_hw_upload_data;
+ u_int8_t rx_hw_upload_data_valid;
+ u_int8_t rx_hw_upload_data_type;
+
+ rx_hw_upload_data = (rxsp->status2 & AR_hw_upload_data) ? 1 : 0;
+ rx_not_sounding = (rxsp->status4 & AR_rx_not_sounding) ? 1 : 0;
+ rx_hw_upload_data_valid = (rxsp->status4 & AR_hw_upload_data_valid) ? 1 : 0;
+ rx_hw_upload_data_type = MS(rxsp->status11, AR_hw_upload_data_type);
+
+ if(rxs->rs_phyerr == 0 && rx_hw_upload_data == 0 &&
+ rx_hw_upload_data_valid == 0 && rx_hw_upload_data_type == 0){
+ return;
+ }
+
+ if(recording && csi_valid == 1)
+ {
+ csi = (struct ath9k_csi*)&csi_buf[csi_head];
+
+ csi->pkt_status.tstamp = rxs->rs_tstamp; // time stamp of the rx packet
+
+ csi->pkt_status.channel = ah->curchan->channel;
+
+ chan_BW = (rxsp->status4 & AR_2040) >> 1;
+ csi->pkt_status.ChanBW = chan_BW; // channel bandwidth
+ nr = ar9300_get_nrx_csi(ah);
+ csi->pkt_status.nr = nr; // rx antennas number
+
+ csi->pkt_status.phyerr = rxs->rs_phyerr; // PHY layer error code
+
+ csi->pkt_status.rssi = rxs->rs_rssi;
+ csi->pkt_status.rssi_ctl0 = rxs->rs_rssi_ctl[0];
+ csi->pkt_status.rssi_ctl1 = rxs->rs_rssi_ctl[1];
+ csi->pkt_status.rssi_ctl2 = rxs->rs_rssi_ctl[2];
+
+ csi->pkt_status.noise = 0; // to be updated
+ csi->pkt_status.rate = rxs->rs_rate; // data rate
+
+ rx_hw_upload_data = (rxsp->status2 & AR_hw_upload_data) ? 1 : 0;
+ rx_not_sounding = (rxsp->status4 & AR_rx_not_sounding) ? 1 : 0;
+ rx_hw_upload_data_valid = (rxsp->status4 & AR_hw_upload_data_valid) ? 1 : 0;
+ rx_hw_upload_data_type = MS(rxsp->status11, AR_hw_upload_data_type);
+
+ // Decides how many tones(subcarriers) are used according to the channel bandwidth
+ if (chan_BW == 0){
+ csi->pkt_status.num_tones = 56; // 20MHz Channel
+ }
+ else if (chan_BW == 1){
+ csi->pkt_status.num_tones = 114; // 40MHz Channel
+ }
+ else{
+ csi->pkt_status.num_tones = 56; // 20MHz Channel
+ printk("Error happends for channel bandwidth recording!!\n");
+ }
+
+ /* tx antennas number
+ * NOTE: when the packet is received with error
+ * The antenna number value is not correct
+ */
+ csi->pkt_status.nc = (int) (rxs->rs_datalen * BITS_PER_BYTE) /
+ (int) (BITS_PER_COMPLEX_SYMBOL * csi->pkt_status.nr * csi->pkt_status.num_tones);
+ printk("bebug_csi: nr is: %d, nc is %d \n\n",csi->pkt_status.nr,csi->pkt_status.nc);
+ /* copy the csi value to the allocated csi buffer */
+ if ( rxs->rs_datalen >0 && rx_hw_upload_data == 1 && rx_hw_upload_data_valid == 1
+ && rx_hw_upload_data_type == 1){
+ csi->pkt_status.csi_len = rxs->rs_datalen;
+ memcpy((void*)(csi->csi_buf),data,rxs->rs_datalen);
+ }else {
+ csi->pkt_status.csi_len = 0;
+ }
+
+ csi_valid = 0; // update
+ csi_head = (csi_head + 1) & 0x0000000F;
+
+ wake_up_interruptible(&csi_queue); // wake up waiting queue
+ }
+}
+EXPORT_SYMBOL(csi_record_status);
+
+
+module_init(csi_init);
+module_exit(csi_exit);
+
+MODULE_AUTHOR("YAXIONG XIE");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CSI EXTRACTION");
+
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.h Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.h
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.h 1969-12-31 19:00:00.000000000 -0500
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.h 2017-02-13 17:33:46.943579002 -0500
@@ -0,0 +1,73 @@
+/*
+ * =====================================================================================
+ * Filename: ar003_csi.h
+ *
+ * Description: ar003_csi data structure definiation
+ * Version: 1.0
+ *
+ * Author: Yaxiong Xie
+ * Email : <xieyaxiongfly@gmail.com>
+ * Organization: WANDS group @ Nanyang Technological University
+ *
+ * Copyright (c) WANDS group @ Nanyang Technological University
+ * =====================================================================================
+ */
+
+#include "hw.h"
+#include "mac.h"
+#include "ar9003_mac.h"
+
+#define DBG_CSI(fmt, args...) printk(fmt,## args)
+#define AR_rx_ness 0x00000060
+#define AR_rx_ness_S 5
+#define AR_ness 0xc0000000
+#define AR_ness_S 30
+#define AR_hw_upload_data 0x00400000
+#define AR_hw_upload_data_S 22
+#define AR_rx_not_sounding 0x00000010
+#define AR_not_sounding 0x20000000
+#define AR_hw_upload_data_valid 0x00000080
+#define AR_hw_upload_data_valid_S 7
+#define AR_hw_upload_data_type 0x06000000
+#define AR_hw_upload_data_type_S 25
+
+#define AR_xmit_data_tries0 0x000f0000
+#define AR_xmit_data_tries0_S 16
+#define AR_xmit_data_tries1 0x00f00000
+#define AR_xmit_data_tries1_S 20
+#define AR_xmit_data_tries2 0x0f000000
+#define AR_xmit_data_tries2_S 24
+#define AR_xmit_data_tries3 0xf0000000
+#define AR_xmit_data_tries3_S 28
+
+struct csi_pkt_status {
+ u_int64_t tstamp; /* h/w assigned timestamp */
+ u_int16_t csi_len; /* csi length */
+ u_int16_t channel; /* receiving channel frequency */
+ u_int8_t phyerr; /* phy error code */
+
+ u_int8_t noise; /* noise floor */
+ u_int8_t rate; /* h/w receive rate index */
+ u_int8_t ChanBW; /* receiving channel bandwidth */
+ u_int8_t num_tones; /* number of tones (subcarriers) */
+ u_int8_t nr; /* number of receiving antennas */
+ u_int8_t nc; /* number of transmitting anteannas */
+
+
+ u_int8_t rssi; /* rx frame RSSI */
+ u_int8_t rssi_ctl0; /* rx frame RSSI [ctl, chain 0] */
+ u_int8_t rssi_ctl1; /* rx frame RSSI [ctl, chain 1] */
+ u_int8_t rssi_ctl2; /* rx frame RSSI [ctl, chain 2] */
+};
+
+struct ath9k_csi {
+ struct csi_pkt_status pkt_status;
+
+ u_int8_t csi_buf[2800]; //buffer for csi value, 3 antena, each with 114 subcarriers, real and imagine part
+ u_int8_t payload_buf[1500]; //buffer for the payload, if you send payload larger than 1500Bytes, change it
+ u_int16_t payload_len;
+
+};
+void csi_record_payload(void* data, u_int16_t data_len);
+void csi_record_status(struct ath_hw *hw, struct ath_rx_status *rxs,struct ar9003_rxs *rxsp,void* data);
+
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_mac.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_mac.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_mac.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_mac.c 2017-02-13 17:33:46.943579002 -0500
@@ -17,6 +17,7 @@
#include "hw.h"
#include "ar9003_mac.h"
#include "ar9003_mci.h"
+#include "ar9003_csi.h"
static void ar9003_hw_rx_enable(struct ath_hw *hw)
{
@@ -30,7 +31,8 @@
int checksum = 0;
u32 val, ctl12, ctl17;
u8 desc_len;
-
+ u_int8_t rate1,rate2,rate3,rate4;
+
desc_len = ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x18 : 0x17);
val = (ATHEROS_VENDOR_ID << AR_DescId_S) |
@@ -61,6 +63,7 @@
ACCESS_ONCE(ads->ctl7) = val;
checksum += (val = (i->buf_len[3] << AR_BufLen_S) & AR_BufLen);
ACCESS_ONCE(ads->ctl9) = val;
+
checksum = (u16) (((checksum & 0xffff) + (checksum >> 16)) & 0xffff);
ACCESS_ONCE(ads->ctl10) = checksum;
@@ -82,7 +85,7 @@
ACCESS_ONCE(ads->ctl14) = 0;
}
- ads->ctl20 = 0;
+ ads->ctl20 = 0;
ads->ctl21 = 0;
ads->ctl22 = 0;
ads->ctl23 = 0;
@@ -150,11 +153,29 @@
| set11nRateFlags(i->rates, 3)
| SM(i->rtscts_rate, AR_RTSCTSRate);
- ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
-
ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1);
ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2);
ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3);
+
+ rate1 = (ads->ctl14 >> 24) & 0xff;
+ rate2 = (ads->ctl14 >> 16) & 0xff;
+ rate3 = (ads->ctl14 >> 8) & 0xff;
+ rate4 = (ads->ctl14 >> 0) & 0xff;
+
+ if ( rate1 >= 0x80 || rate2 >= 0x80 || rate3 >= 0x80){
+ ACCESS_ONCE(ads->ctl19) = 0;
+ ACCESS_ONCE(ads->ctl13) &= ~(AR_xmit_data_tries1 | AR_xmit_data_tries2 | AR_xmit_data_tries3);
+ ACCESS_ONCE(ads->ctl20) &= 0x3f000000;
+ ACCESS_ONCE(ads->ctl21) &= 0x3f000000;
+ ACCESS_ONCE(ads->ctl22) &= 0x3f000000;
+ }else{
+ ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
+ }
+ if ( rate4 >= 0x80){
+ ACCESS_ONCE(ads->ctl19) = 0;
+ }else{
+ ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
+ }
}
static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
@@ -483,6 +504,9 @@
struct ar9003_rxs *rxsp = (struct ar9003_rxs *) buf_addr;
unsigned int phyerr;
+ void *data_addr;
+ u_int16_t data_len;
+
if ((rxsp->status11 & AR_RxDone) == 0)
return -EINPROGRESS;
@@ -577,9 +601,25 @@
}
}
}
-
- if (rxsp->status11 & AR_KeyMiss)
+
+ if (rxsp->status11 & AR_KeyMiss)
rxs->rs_status |= ATH9K_RXERR_KEYMISS;
+
+ data_len = rxs->rs_datalen;
+ data_addr = buf_addr + 48;
+
+ if (rxsp->status11 & AR_CRCErr){
+ if (rxs->rs_rate >= 0x80){
+ csi_record_payload(data_addr,data_len);
+ csi_record_status(ah,rxs,rxsp,data_addr);
+ }
+ }else{
+ if (rxs->rs_more == 1)
+ csi_record_payload(data_addr,data_len);
+
+ if (rxs->rs_rate >= 0x80)
+ csi_record_status(ah,rxs,rxsp,data_addr);
+ }
return 0;
}
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/hw.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/hw.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/hw.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/hw.c 2017-02-13 17:33:46.959579002 -0500
@@ -1818,6 +1818,7 @@
u32 saveLedState;
u32 saveDefAntenna;
u32 macStaId1;
+ u32 tmp;
u64 tsf = 0;
s64 usec = 0;
int r;
@@ -2028,6 +2029,10 @@
ah->radar_conf.ext_channel = IS_CHAN_HT40(chan);
ath9k_hw_set_radar_params(ah);
}
+ //csi_debug
+ tmp = REG_READ(ah,0x8344);
+ tmp |= (1 << 28);
+ REG_WRITE(ah, 0x8344,tmp);
return 0;
}
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/init.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/init.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/init.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/init.c 2017-02-13 17:33:46.959579002 -0500
@@ -831,7 +831,7 @@
hw->flags |= IEEE80211_HW_SUPPORTS_PS;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
- hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+ //hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
if (AR_SREV_9280_20_OR_LATER(ah))
hw->radiotap_mcs_details |=
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/main.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/main.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/main.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/main.c 2017-02-13 17:33:46.967579002 -0500
@@ -2116,7 +2116,6 @@
bf = avp->av_bcbuf;
if (!bf || !bf->bf_mpdu)
goto skip;
-
status = ath9k_hw_txprocdesc(ah, bf->bf_desc, &ts);
if (status == -EINPROGRESS)
goto skip;
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/Makefile Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/Makefile
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/Makefile 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/Makefile 2017-02-13 17:33:46.931579002 -0500
@@ -1,3 +1,5 @@
+obj-m += ar9003_csi.o
+
ath9k-y += beacon.o \
gpio.o \
init.o \
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/xmit.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/xmit.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/xmit.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/xmit.c 2017-02-13 17:33:46.967579002 -0500
@@ -2681,7 +2681,7 @@
lastbf = bf->bf_lastbf;
ds = lastbf->bf_desc;
-
+
memset(&ts, 0, sizeof(ts));
status = ath9k_hw_txprocdesc(ah, ds, &ts);
if (status == -EINPROGRESS)
@@ -2736,7 +2736,6 @@
for (;;) {
if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
break;
-
status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts);
if (status == -EINPROGRESS)
break;
Kernel Version 4.1.10
Diff with the origin... Could it be ported?
diff -uNr linux-4.1.10/arch/sh/boot/compressed/vmlinux.scr Atheros-CSI-Tool/arch/sh/boot/compressed/vmlinux.scr
--- linux-4.1.10/arch/sh/boot/compressed/vmlinux.scr 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/arch/sh/boot/compressed/vmlinux.scr 1969-12-31 19:00:00.000000000 -0500
@@ -1,10 +0,0 @@
-SECTIONS
-{
- .rodata..compressed : {
- input_len = .;
- LONG(input_data_end - input_data) input_data = .;
- *(.data)
- output_len = . - 4;
- input_data_end = .;
- }
-}
diff -uNr linux-4.1.10/arch/sh/boot/romimage/vmlinux.scr Atheros-CSI-Tool/arch/sh/boot/romimage/vmlinux.scr
--- linux-4.1.10/arch/sh/boot/romimage/vmlinux.scr 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/arch/sh/boot/romimage/vmlinux.scr 1969-12-31 19:00:00.000000000 -0500
@@ -1,8 +0,0 @@
-SECTIONS
-{
- .text : {
- zero_page_pos = .;
- *(.data)
- end_data = .;
- }
-}
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.c 1969-12-31 19:00:00.000000000 -0500
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.c 2017-02-13 17:33:46.943579002 -0500
@@ -0,0 +1,325 @@
+/*
+ * =====================================================================================
+ * Filename: ath9k_csi.c
+ *
+ * Description: extrac csi and data together from hardware
+ * Version: 1.0
+ *
+ * Author: Yaxiong Xie
+ * Email : <xieyaxiongfly@gmail.com>
+ * Organization: WANDS group @ Nanyang Technological University
+ *
+ * Copyright (c) WANDS group @ Nanyang Technological University
+ * =====================================================================================
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/netdevice.h>
+
+#include "ar9003_csi.h"
+#include "ar9003_mac.h"
+#include "ar9003_phy.h"
+#include "mac.h"
+#include "hw.h"
+
+#define Tx_Buf_LEN 20480 // Generally, buffere with 20480 bits is enough
+ // You can change the size freely
+
+//#define Rx_Buf_LEN 128 // Generally, buffere with 20480 bits is enough
+
+#define BITS_PER_BYTE 8
+#define BITS_PER_SYMBOL 10
+#define BITS_PER_COMPLEX_SYMBOL (2 * BITS_PER_SYMBOL)
+
+#define DEVICE_NAME "CSI_dev"
+#define CLASS_NAME "CSI_class"
+#define AH_MAX_CHAINS 3 //maximum chain number, we set it to 3
+#define NUM_OF_CHAINMASK (1 << AH_MAX_CHAINS)
+
+
+volatile u32 csi_head;
+volatile u32 csi_tail;
+volatile u32 csi_len;
+volatile u32 csi_valid;
+volatile u32 recording;
+
+static struct ath9k_csi csi_buf[16];
+static char tx_buf[Tx_Buf_LEN];
+//static char rx_buf[Rx_Buf_LEN];
+
+static int majorNumber;
+static struct class* ebbcharClass = NULL;
+static struct device* ebbcharDevice = NULL;
+
+DECLARE_WAIT_QUEUE_HEAD(csi_queue);
+
+static int csi_open(struct inode *inode, struct file *file);
+static int csi_close(struct inode *inode, struct file *file);
+static ssize_t csi_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos);
+static ssize_t csi_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos);
+
+static const struct file_operations csi_fops = {
+ .read = csi_read,
+ .write = csi_write,
+ .open = csi_open,
+ .release = csi_close,
+ .llseek = default_llseek,
+};
+
+static u_int8_t Num_bits_on[NUM_OF_CHAINMASK] = {
+ 0 /* 000 */,
+ 1 /* 001 */,
+ 1 /* 010 */,
+ 2 /* 011 */,
+ 1 /* 100 */,
+ 2 /* 101 */,
+ 2 /* 110 */,
+ 3 /* 111 */
+};
+
+u_int8_t ar9300_get_nrx_csi(struct ath_hw *ah)
+{
+ return Num_bits_on[ah->rxchainmask];
+}
+
+static int __init csi_init(void)
+{
+
+ // initalize parameters
+ csi_head = 0;
+ csi_tail = 0;
+ recording = 0;
+ csi_valid = 0;
+
+ // Try to dynamically allocate a major number for the device -- more difficult but worth it
+ majorNumber = register_chrdev(0, DEVICE_NAME, &csi_fops);
+ if (majorNumber<0){
+ printk(KERN_ALERT "debug_csi: failed to register a major number\n");
+ return majorNumber;
+ }
+ printk(KERN_INFO "debug_csi: registered correctly with major number %d\n", majorNumber);
+
+ // Register the device class
+ ebbcharClass = class_create(THIS_MODULE, CLASS_NAME);
+ if (IS_ERR(ebbcharClass)){ // Check for error and clean up if there is
+ unregister_chrdev(majorNumber, DEVICE_NAME);
+ printk(KERN_ALERT "debug_csi: Failed to register device class\n");
+ return PTR_ERR(ebbcharClass); // Correct way to return an error on a pointer
+ }
+ printk(KERN_INFO "debug_csi: device class registered correctly\n");
+
+ // Register the device driver
+ ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
+ if (IS_ERR(ebbcharDevice)){ // Clean up if there is an error
+ class_destroy(ebbcharClass); // Repeated code but the alternative is goto statements
+ unregister_chrdev(majorNumber, DEVICE_NAME);
+ printk(KERN_ALERT "Failed to create the device\n");
+ return PTR_ERR(ebbcharDevice);
+ }
+ printk(KERN_INFO "debug_csi: device class created correctly \n"); // Made it! device was initialized
+
+ return 0;
+}
+
+static void __exit csi_exit(void)
+{
+ /* delete and unregister the devices we have created and registered */
+ device_destroy(ebbcharClass, MKDEV(majorNumber, 0)); // remove the device
+ class_unregister(ebbcharClass); // unregister the device class
+ class_destroy(ebbcharClass); // remove the device class
+ unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
+ printk(KERN_INFO "debug_csi: Goodbye CSI device!\n");
+
+}
+
+static int csi_open(struct inode *inode, struct file *file)
+{
+ printk(KERN_ALERT "debug_csi: csi open! \n");
+ recording = 1; // we can begin to record when
+ // the devices is open
+ return 0;
+}
+
+static int csi_close(struct inode *inode, struct file *file)
+{
+ printk(KERN_ALERT "debug_csi: csi close! \n");
+ recording = 0; // close and reset
+ return 0;
+}
+
+static ssize_t csi_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ u_int16_t len;
+ u_int8_t *csi_buf_addr;
+ u_int8_t *payload_buf_addr;
+ u_int16_t csi_len, payload_len;
+ struct ath9k_csi* csi;
+ struct csi_pkt_status *RxStatus;
+
+ *ppos = 0;
+
+ if (csi_head == csi_tail)
+ { // wait until time out
+ wait_event_interruptible_timeout(csi_queue,csi_head != csi_tail, 5*HZ);
+ }
+ if(csi_head != csi_tail){
+ csi = (struct ath9k_csi*)&csi_buf[csi_tail];
+ len = 0;
+
+ RxStatus = &(csi->pkt_status); // the status struct
+
+ csi_len = RxStatus->csi_len; // csi length (bytes)
+ csi_buf_addr = csi->csi_buf; // csi buffer
+ payload_len = csi->payload_len; // payload length (bytes)
+ payload_buf_addr = csi->payload_buf; // payload buffer
+
+
+ memcpy(tx_buf,RxStatus,23); // copy the status to the buffer
+ len += 23;
+
+ memcpy(tx_buf+len,&payload_len, 2); // record the length of payload
+ len += 2;
+
+ if (csi_len > 0){
+ memcpy(tx_buf+len,csi_buf_addr,csi_len); // copy csi to the buffer
+ len += csi_len;
+ }
+
+ memcpy(tx_buf+len,payload_buf_addr, payload_len); // copy payload to the buffer
+ len += payload_len;
+
+ memcpy(tx_buf+len,&len, 2); // record how many bytes we copy
+ len += 2;
+
+ copy_to_user(user_buf,tx_buf,len); // COPY
+
+ csi_tail = (csi_tail+1) & 0x0000000F; // delete the buffer
+ return len;
+ }else{
+ return 0;
+ }
+}
+
+static ssize_t csi_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ printk(KERN_ALERT "debug_csi: csi write!\n");
+ return 0;
+}
+
+void csi_record_payload(void* data, u_int16_t data_len)
+{
+ struct ath9k_csi* csi;
+ if(recording )
+ {
+ if( ((csi_head + 1) & 0x0000000F) == csi_tail) // check and update
+ csi_tail = (csi_tail + 1) & 0x0000000F;
+
+ csi = (struct ath9k_csi*)&csi_buf[csi_head];
+ memcpy((void*)(csi->payload_buf),data, data_len); // copy the payload
+ csi->payload_len = data_len; // record the payload length (bytes)
+ csi_valid = 1;
+ }
+}
+EXPORT_SYMBOL(csi_record_payload);
+
+void csi_record_status(struct ath_hw *ah, struct ath_rx_status *rxs, struct ar9003_rxs *rxsp,void* data)
+{
+ struct ath9k_csi* csi;
+
+ u_int8_t nr;
+ u_int8_t chan_BW;
+ u_int8_t rx_not_sounding;
+ u_int8_t rx_hw_upload_data;
+ u_int8_t rx_hw_upload_data_valid;
+ u_int8_t rx_hw_upload_data_type;
+
+ rx_hw_upload_data = (rxsp->status2 & AR_hw_upload_data) ? 1 : 0;
+ rx_not_sounding = (rxsp->status4 & AR_rx_not_sounding) ? 1 : 0;
+ rx_hw_upload_data_valid = (rxsp->status4 & AR_hw_upload_data_valid) ? 1 : 0;
+ rx_hw_upload_data_type = MS(rxsp->status11, AR_hw_upload_data_type);
+
+ if(rxs->rs_phyerr == 0 && rx_hw_upload_data == 0 &&
+ rx_hw_upload_data_valid == 0 && rx_hw_upload_data_type == 0){
+ return;
+ }
+
+ if(recording && csi_valid == 1)
+ {
+ csi = (struct ath9k_csi*)&csi_buf[csi_head];
+
+ csi->pkt_status.tstamp = rxs->rs_tstamp; // time stamp of the rx packet
+
+ csi->pkt_status.channel = ah->curchan->channel;
+
+ chan_BW = (rxsp->status4 & AR_2040) >> 1;
+ csi->pkt_status.ChanBW = chan_BW; // channel bandwidth
+ nr = ar9300_get_nrx_csi(ah);
+ csi->pkt_status.nr = nr; // rx antennas number
+
+ csi->pkt_status.phyerr = rxs->rs_phyerr; // PHY layer error code
+
+ csi->pkt_status.rssi = rxs->rs_rssi;
+ csi->pkt_status.rssi_ctl0 = rxs->rs_rssi_ctl[0];
+ csi->pkt_status.rssi_ctl1 = rxs->rs_rssi_ctl[1];
+ csi->pkt_status.rssi_ctl2 = rxs->rs_rssi_ctl[2];
+
+ csi->pkt_status.noise = 0; // to be updated
+ csi->pkt_status.rate = rxs->rs_rate; // data rate
+
+ rx_hw_upload_data = (rxsp->status2 & AR_hw_upload_data) ? 1 : 0;
+ rx_not_sounding = (rxsp->status4 & AR_rx_not_sounding) ? 1 : 0;
+ rx_hw_upload_data_valid = (rxsp->status4 & AR_hw_upload_data_valid) ? 1 : 0;
+ rx_hw_upload_data_type = MS(rxsp->status11, AR_hw_upload_data_type);
+
+ // Decides how many tones(subcarriers) are used according to the channel bandwidth
+ if (chan_BW == 0){
+ csi->pkt_status.num_tones = 56; // 20MHz Channel
+ }
+ else if (chan_BW == 1){
+ csi->pkt_status.num_tones = 114; // 40MHz Channel
+ }
+ else{
+ csi->pkt_status.num_tones = 56; // 20MHz Channel
+ printk("Error happends for channel bandwidth recording!!\n");
+ }
+
+ /* tx antennas number
+ * NOTE: when the packet is received with error
+ * The antenna number value is not correct
+ */
+ csi->pkt_status.nc = (int) (rxs->rs_datalen * BITS_PER_BYTE) /
+ (int) (BITS_PER_COMPLEX_SYMBOL * csi->pkt_status.nr * csi->pkt_status.num_tones);
+ printk("bebug_csi: nr is: %d, nc is %d \n\n",csi->pkt_status.nr,csi->pkt_status.nc);
+ /* copy the csi value to the allocated csi buffer */
+ if ( rxs->rs_datalen >0 && rx_hw_upload_data == 1 && rx_hw_upload_data_valid == 1
+ && rx_hw_upload_data_type == 1){
+ csi->pkt_status.csi_len = rxs->rs_datalen;
+ memcpy((void*)(csi->csi_buf),data,rxs->rs_datalen);
+ }else {
+ csi->pkt_status.csi_len = 0;
+ }
+
+ csi_valid = 0; // update
+ csi_head = (csi_head + 1) & 0x0000000F;
+
+ wake_up_interruptible(&csi_queue); // wake up waiting queue
+ }
+}
+EXPORT_SYMBOL(csi_record_status);
+
+
+module_init(csi_init);
+module_exit(csi_exit);
+
+MODULE_AUTHOR("YAXIONG XIE");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CSI EXTRACTION");
+
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.h Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.h
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_csi.h 1969-12-31 19:00:00.000000000 -0500
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_csi.h 2017-02-13 17:33:46.943579002 -0500
@@ -0,0 +1,73 @@
+/*
+ * =====================================================================================
+ * Filename: ar003_csi.h
+ *
+ * Description: ar003_csi data structure definiation
+ * Version: 1.0
+ *
+ * Author: Yaxiong Xie
+ * Email : <xieyaxiongfly@gmail.com>
+ * Organization: WANDS group @ Nanyang Technological University
+ *
+ * Copyright (c) WANDS group @ Nanyang Technological University
+ * =====================================================================================
+ */
+
+#include "hw.h"
+#include "mac.h"
+#include "ar9003_mac.h"
+
+#define DBG_CSI(fmt, args...) printk(fmt,## args)
+#define AR_rx_ness 0x00000060
+#define AR_rx_ness_S 5
+#define AR_ness 0xc0000000
+#define AR_ness_S 30
+#define AR_hw_upload_data 0x00400000
+#define AR_hw_upload_data_S 22
+#define AR_rx_not_sounding 0x00000010
+#define AR_not_sounding 0x20000000
+#define AR_hw_upload_data_valid 0x00000080
+#define AR_hw_upload_data_valid_S 7
+#define AR_hw_upload_data_type 0x06000000
+#define AR_hw_upload_data_type_S 25
+
+#define AR_xmit_data_tries0 0x000f0000
+#define AR_xmit_data_tries0_S 16
+#define AR_xmit_data_tries1 0x00f00000
+#define AR_xmit_data_tries1_S 20
+#define AR_xmit_data_tries2 0x0f000000
+#define AR_xmit_data_tries2_S 24
+#define AR_xmit_data_tries3 0xf0000000
+#define AR_xmit_data_tries3_S 28
+
+struct csi_pkt_status {
+ u_int64_t tstamp; /* h/w assigned timestamp */
+ u_int16_t csi_len; /* csi length */
+ u_int16_t channel; /* receiving channel frequency */
+ u_int8_t phyerr; /* phy error code */
+
+ u_int8_t noise; /* noise floor */
+ u_int8_t rate; /* h/w receive rate index */
+ u_int8_t ChanBW; /* receiving channel bandwidth */
+ u_int8_t num_tones; /* number of tones (subcarriers) */
+ u_int8_t nr; /* number of receiving antennas */
+ u_int8_t nc; /* number of transmitting anteannas */
+
+
+ u_int8_t rssi; /* rx frame RSSI */
+ u_int8_t rssi_ctl0; /* rx frame RSSI [ctl, chain 0] */
+ u_int8_t rssi_ctl1; /* rx frame RSSI [ctl, chain 1] */
+ u_int8_t rssi_ctl2; /* rx frame RSSI [ctl, chain 2] */
+};
+
+struct ath9k_csi {
+ struct csi_pkt_status pkt_status;
+
+ u_int8_t csi_buf[2800]; //buffer for csi value, 3 antena, each with 114 subcarriers, real and imagine part
+ u_int8_t payload_buf[1500]; //buffer for the payload, if you send payload larger than 1500Bytes, change it
+ u_int16_t payload_len;
+
+};
+void csi_record_payload(void* data, u_int16_t data_len);
+void csi_record_status(struct ath_hw *hw, struct ath_rx_status *rxs,struct ar9003_rxs *rxsp,void* data);
+
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_mac.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_mac.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/ar9003_mac.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/ar9003_mac.c 2017-02-13 17:33:46.943579002 -0500
@@ -17,6 +17,7 @@
#include "hw.h"
#include "ar9003_mac.h"
#include "ar9003_mci.h"
+#include "ar9003_csi.h"
static void ar9003_hw_rx_enable(struct ath_hw *hw)
{
@@ -30,7 +31,8 @@
int checksum = 0;
u32 val, ctl12, ctl17;
u8 desc_len;
-
+ u_int8_t rate1,rate2,rate3,rate4;
+
desc_len = ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x18 : 0x17);
val = (ATHEROS_VENDOR_ID << AR_DescId_S) |
@@ -61,6 +63,7 @@
ACCESS_ONCE(ads->ctl7) = val;
checksum += (val = (i->buf_len[3] << AR_BufLen_S) & AR_BufLen);
ACCESS_ONCE(ads->ctl9) = val;
+
checksum = (u16) (((checksum & 0xffff) + (checksum >> 16)) & 0xffff);
ACCESS_ONCE(ads->ctl10) = checksum;
@@ -82,7 +85,7 @@
ACCESS_ONCE(ads->ctl14) = 0;
}
- ads->ctl20 = 0;
+ ads->ctl20 = 0;
ads->ctl21 = 0;
ads->ctl22 = 0;
ads->ctl23 = 0;
@@ -150,11 +153,29 @@
| set11nRateFlags(i->rates, 3)
| SM(i->rtscts_rate, AR_RTSCTSRate);
- ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
-
ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1);
ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2);
ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3);
+
+ rate1 = (ads->ctl14 >> 24) & 0xff;
+ rate2 = (ads->ctl14 >> 16) & 0xff;
+ rate3 = (ads->ctl14 >> 8) & 0xff;
+ rate4 = (ads->ctl14 >> 0) & 0xff;
+
+ if ( rate1 >= 0x80 || rate2 >= 0x80 || rate3 >= 0x80){
+ ACCESS_ONCE(ads->ctl19) = 0;
+ ACCESS_ONCE(ads->ctl13) &= ~(AR_xmit_data_tries1 | AR_xmit_data_tries2 | AR_xmit_data_tries3);
+ ACCESS_ONCE(ads->ctl20) &= 0x3f000000;
+ ACCESS_ONCE(ads->ctl21) &= 0x3f000000;
+ ACCESS_ONCE(ads->ctl22) &= 0x3f000000;
+ }else{
+ ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
+ }
+ if ( rate4 >= 0x80){
+ ACCESS_ONCE(ads->ctl19) = 0;
+ }else{
+ ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
+ }
}
static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
@@ -483,6 +504,9 @@
struct ar9003_rxs *rxsp = (struct ar9003_rxs *) buf_addr;
unsigned int phyerr;
+ void *data_addr;
+ u_int16_t data_len;
+
if ((rxsp->status11 & AR_RxDone) == 0)
return -EINPROGRESS;
@@ -577,9 +601,25 @@
}
}
}
-
- if (rxsp->status11 & AR_KeyMiss)
+
+ if (rxsp->status11 & AR_KeyMiss)
rxs->rs_status |= ATH9K_RXERR_KEYMISS;
+
+ data_len = rxs->rs_datalen;
+ data_addr = buf_addr + 48;
+
+ if (rxsp->status11 & AR_CRCErr){
+ if (rxs->rs_rate >= 0x80){
+ csi_record_payload(data_addr,data_len);
+ csi_record_status(ah,rxs,rxsp,data_addr);
+ }
+ }else{
+ if (rxs->rs_more == 1)
+ csi_record_payload(data_addr,data_len);
+
+ if (rxs->rs_rate >= 0x80)
+ csi_record_status(ah,rxs,rxsp,data_addr);
+ }
return 0;
}
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/hw.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/hw.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/hw.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/hw.c 2017-02-13 17:33:46.959579002 -0500
@@ -1818,6 +1818,7 @@
u32 saveLedState;
u32 saveDefAntenna;
u32 macStaId1;
+ u32 tmp;
u64 tsf = 0;
s64 usec = 0;
int r;
@@ -2028,6 +2029,10 @@
ah->radar_conf.ext_channel = IS_CHAN_HT40(chan);
ath9k_hw_set_radar_params(ah);
}
+ //csi_debug
+ tmp = REG_READ(ah,0x8344);
+ tmp |= (1 << 28);
+ REG_WRITE(ah, 0x8344,tmp);
return 0;
}
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/init.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/init.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/init.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/init.c 2017-02-13 17:33:46.959579002 -0500
@@ -831,7 +831,7 @@
hw->flags |= IEEE80211_HW_SUPPORTS_PS;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
- hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+ //hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
if (AR_SREV_9280_20_OR_LATER(ah))
hw->radiotap_mcs_details |=
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/main.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/main.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/main.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/main.c 2017-02-13 17:33:46.967579002 -0500
@@ -2116,7 +2116,6 @@
bf = avp->av_bcbuf;
if (!bf || !bf->bf_mpdu)
goto skip;
-
status = ath9k_hw_txprocdesc(ah, bf->bf_desc, &ts);
if (status == -EINPROGRESS)
goto skip;
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/Makefile Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/Makefile
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/Makefile 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/Makefile 2017-02-13 17:33:46.931579002 -0500
@@ -1,3 +1,5 @@
+obj-m += ar9003_csi.o
+
ath9k-y += beacon.o \
gpio.o \
init.o \
diff -uNr linux-4.1.10/drivers/net/wireless/ath/ath9k/xmit.c Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/xmit.c
--- linux-4.1.10/drivers/net/wireless/ath/ath9k/xmit.c 2015-10-03 07:49:38.000000000 -0400
+++ Atheros-CSI-Tool/drivers/net/wireless/ath/ath9k/xmit.c 2017-02-13 17:33:46.967579002 -0500
@@ -2681,7 +2681,7 @@
lastbf = bf->bf_lastbf;
ds = lastbf->bf_desc;
-
+
memset(&ts, 0, sizeof(ts));
status = ath9k_hw_txprocdesc(ah, ds, &ts);
if (status == -EINPROGRESS)
@@ -2736,7 +2736,6 @@
for (;;) {
if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
break;
-
status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts);
if (status == -EINPROGRESS)
break;
Subscribe to:
Posts (Atom)