/*
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
* Copyright (C) 2023-2024 Miroslav Hutar.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "MainController.h"
#include "Arguments.h"
#include "Logger.h"
#include "gui/MainWindow.h"
#include "layout.h"
#include
#include
#include
MainController::MainController()
{
signal(SIGINT, this->intHandler);
}
MainController *MainController::getInstance()
{
if (INSTANCE == nullptr)
{
INSTANCE = new MainController();
}
return INSTANCE;
}
gint MainController::updatePlots()
{
WiFiCsiController::csiQueueMutex.lock();
if (!WiFiCsiController::csiQueue.empty())
{
if (csiToPlot)
{
delete csiToPlot;
}
csiToPlot = WiFiCsiController::csiQueue.front();
WiFiCsiController::csiQueue.pop();
}
//clear old values
while (!WiFiCsiController::csiQueue.empty()) {
delete WiFiCsiController::csiQueue.front();
WiFiCsiController::csiQueue.pop();
}
WiFiCsiController::csiQueueMutex.unlock();
if (!csiToPlot)
{
return (TRUE);
}
MainController *mainController = MainController::getInstance();
mainController->plotAmplitude->updateData(csiToPlot, &csiToPlot->magnitude);
mainController->plotPhase->updateData(csiToPlot, &csiToPlot->phase);
// force refresh, not wait on next frame redraw
/* GtkAllocation reg;
gtk_widget_get_allocation((GtkWidget *)mainController->plotAmplitude->get_parent()->gobj(), ®);
cairo_region_t *creg = cairo_region_create_rectangle(®);
GdkWindow *pUnderlyingWindow = gtk_widget_get_window((GtkWidget *)mainController->plotAmplitude->get_parent()->gobj());
cairo_t *cr = gdk_cairo_create(pUnderlyingWindow);
gdk_cairo_region(cr, creg);
cairo_clip(cr);
gtk_widget_draw((GtkWidget *)mainController->plotAmplitude->get_parent()->gobj(), cr);
cairo_destroy(cr);
cairo_region_destroy(creg); */
return (TRUE);
}
void MainController::initPlots()
{
Glib::RefPtr plotBox = Glib::RefPtr::cast_dynamic(this->mainWindow->builder->get_object("plotBox"));
MainController *mainController = MainController::getInstance();
mainController->plotAmplitude->yLabel = "Amplitude";
mainController->plotAmplitude->title = "Amplitude";
mainController->plotAmplitude->init(plotBox);
mainController->plotPhase->yLabel = "Phase (rad)";
mainController->plotPhase->title = "Phase";
mainController->plotPhase->yTicksMin = -4;
mainController->plotPhase->yTicksMax = 4;
mainController->plotPhase->init(plotBox);
this->updatePlotsSourceId = gdk_threads_add_idle((GSourceFunc)MainController::updatePlots, nullptr);
}
void MainController::deleteInstance()
{
MainController *mainController = MainController::INSTANCE;
if (mainController->measuring)
{
MainController::INSTANCE->measureCsi(true);
}
if (mainController->injecting)
{
MainController::INSTANCE->injectPackets(true);
}
if (Arguments::arguments.plot)
{
MainController::INSTANCE->app->quit();
}
delete MainController::INSTANCE;
}
void MainController::runNoGui(bool detach)
{
this->initInterface();
if (Arguments::arguments.plot)
{
gtk_init(NULL, NULL);
Glib::init();
this->plotAmplitude = new Plot();
this->plotPhase = new Plot();
this->app = Gtk::Application::create("com.kuskosoft.feitcsi");
std::string layout((char *)MainWindow_glade);
//auto builder = Gtk::Builder::create_from_file("MainWindow.glade");
auto builder = Gtk::Builder::create_from_string(layout);
builder->get_widget_derived("MainWindow", this->mainWindow);
this->initPlots();
if (Arguments::arguments.measure)
{
this->measureCsi();
}
if (Arguments::arguments.inject)
{
this->injectPackets();
}
this->app->run(*this->mainWindow);
return;
}
if (Arguments::arguments.measure)
{
this->measuring = true;
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
}
if (Arguments::arguments.inject)
{
this->injecting = true;
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
}
if (Arguments::arguments.measure)
{
if (detach) {
pthread_detach(this->measureCsiThread);
} else {
pthread_join(this->measureCsiThread, NULL);
}
}
if (Arguments::arguments.inject)
{
if (detach) {
pthread_detach(this->injectPacketThread);
} else {
pthread_join(this->injectPacketThread, NULL);
}
}
}
void MainController::measureCsi(bool stop)
{
if (stop)
{
this->measuring = false;
pthread_cancel(this->measureCsiThread);
}
else
{
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
this->measuring = true;
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
pthread_detach(this->measureCsiThread);
}
}
void MainController::injectPackets(bool stop)
{
this->wifiController.setTxPower();
if (stop)
{
this->injecting = false;
pthread_cancel(this->injectPacketThread);
}
else
{
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
this->injecting = true;
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
pthread_detach(this->injectPacketThread);
}
}
void MainController::runGui()
{
this->initInterface();
Arguments::arguments.plot = true;
Arguments::arguments.verbose = true;
Arguments::arguments.measure = false;
Arguments::arguments.inject = false;
gtk_init(NULL, NULL);
Glib::init();
this->plotAmplitude = new Plot();
this->plotPhase = new Plot();
this->app = Gtk::Application::create("com.kuskosoft.feitcsi");
std::string layout((char*)MainWindow_glade);
//auto builder = Gtk::Builder::create_from_file("MainWindow.glade");
auto builder = Gtk::Builder::create_from_string(layout);
builder->get_widget_derived("MainWindow", this->mainWindow);
this->initPlots();
//g_signal_connect(app, "delete-event", G_CALLBACK(MainController::deleteInstance), NULL);
this->app->run(*this->mainWindow);
}
void MainController::runUdpSocket()
{
this->udpSocket = new UdpSocket();
udpSocket->init();
}
void MainController::initInterface()
{
try
{
this->wifiController.init();
this->wifiController.getInterfaces();
this->wifiController.getPhys();
// delete actual interfaces
for (InterfaceInfo interface : this->wifiController.interfaces)
{
if (Arguments::arguments.verbose)
{
Logger::log(info) << "Remove interface " << interface.ifName << "\n";
}
this->bkpInterfaces.push_back(interface);
this->wifiController.removeInterface(NULL, interface.ifIndex);
}
this->wifiController.addMonitorDevice(MONITOR_INTERFACE_NAME, NL80211_IFTYPE_MONITOR);
this->wifiController.setInterfaceUpDown(MONITOR_INTERFACE_NAME, true);
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
if (!MainController::mainWindow)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500)); //wait on init then set power and go next
this->wifiController.setTxPower();
}
}
catch(const std::exception& e)
{
if (MainController::mainWindow)
{
MainController::mainWindow->fatalError(e.what());
}
else
{
Logger::log(error) << e.what() << '\n';
delete MainController::INSTANCE;
exit(1);
}
}
}
void *MainController::measureCsi(void *arg)
{
try
{
WiFiCsiController wcs;
wcs.init();
wcs.listenToCsi();
}
catch (const std::exception &e)
{
if (MainController::mainWindow)
{
MainController::mainWindow->fatalError(e.what());
}
else
{
Logger::log(error) << e.what() << '\n';
}
}
return 0;
}
void *MainController::injectPackets(void *arg)
{
try
{
PacketInjector pi;
if (Arguments::arguments.injectRepeat)
{
for (uint32_t i = 0; i < Arguments::arguments.injectRepeat; i++)
{
pi.inject();
std::this_thread::sleep_for(std::chrono::microseconds(Arguments::arguments.injectDelay));
}
}
else
{
while (true)
{
pi.inject();
std::this_thread::sleep_for(std::chrono::microseconds(Arguments::arguments.injectDelay));
}
}
}
catch(const std::exception& e)
{
if (MainController::mainWindow)
{
MainController::mainWindow->fatalError(e.what());
}
else
{
Logger::log(error) << e.what() << '\n';
}
}
return 0;
}
void MainController::restoreState()
{
MainController *mainController = MainController::getInstance();
if (mainController->measureCsiThread)
{
pthread_cancel(mainController->measureCsiThread);
}
if (mainController->injectPacketThread)
{
pthread_cancel(mainController->injectPacketThread);
}
mainController->wifiController.removeInterface(MONITOR_INTERFACE_NAME);
for (InterfaceInfo interface : mainController->bkpInterfaces)
{
if (Arguments::arguments.verbose)
{
Logger::log(info) << "Recovering interface " << interface.ifName << "\n";
}
mainController->wifiController.addMonitorDevice(interface.ifName.c_str(), (nl80211_iftype)interface.ifType);
}
mainController->bkpInterfaces.clear();
mainController->wifiController.interfaces.clear();
if (Arguments::arguments.verbose)
{
Logger::log(info) << "Exiting recovery state...\n";
}
}
MainController::~MainController()
{
this->restoreState();
if (udpSocket) {
delete udpSocket;
}
if (csiToPlot)
{
delete csiToPlot;
}
}
void MainController::intHandler(int dummy)
{
MainController::INSTANCE->deleteInstance();
exit(0);
}