File indexing completed on 2024-04-28 03:52:06

0001 /*
0002  * BluezQt - Asynchronous Bluez wrapper library
0003  *
0004  * SPDX-FileCopyrightText: 2015 David Rosca <nowrep@gmail.com>
0005  *
0006  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007  */
0008 
0009 #include "rfkill.h"
0010 #include "rfkill_p.h"
0011 
0012 #include "debug.h"
0013 
0014 #ifdef Q_OS_LINUX
0015 #include <fcntl.h>
0016 #include <stdint.h>
0017 #include <string.h>
0018 #include <unistd.h>
0019 #endif
0020 
0021 #include <QSocketNotifier>
0022 
0023 namespace BluezQt
0024 {
0025 #ifdef Q_OS_LINUX
0026 enum rfkill_type {
0027     RFKILL_TYPE_ALL = 0,
0028     RFKILL_TYPE_WLAN,
0029     RFKILL_TYPE_BLUETOOTH,
0030     RFKILL_TYPE_UWB,
0031     RFKILL_TYPE_WIMAX,
0032     RFKILL_TYPE_WWAN,
0033 };
0034 
0035 enum rfkill_operation {
0036     RFKILL_OP_ADD = 0,
0037     RFKILL_OP_DEL,
0038     RFKILL_OP_CHANGE,
0039     RFKILL_OP_CHANGE_ALL,
0040 };
0041 
0042 struct rfkill_event {
0043     quint32 idx;
0044     quint8 type;
0045     quint8 op;
0046     quint8 soft;
0047     quint8 hard;
0048 };
0049 #endif
0050 
0051 Rfkill::Rfkill(QObject *parent)
0052     : QObject(parent)
0053     , d(new RfkillPrivate)
0054 {
0055     init();
0056 }
0057 
0058 Rfkill::~Rfkill()
0059 {
0060 #ifdef Q_OS_LINUX
0061     if (d->m_readFd != -1) {
0062         ::close(d->m_readFd);
0063     }
0064 
0065     if (d->m_writeFd != -1) {
0066         ::close(d->m_writeFd);
0067     }
0068 #endif
0069 }
0070 
0071 Rfkill::State Rfkill::state() const
0072 {
0073     return d->m_state;
0074 }
0075 
0076 void Rfkill::block()
0077 {
0078     if (d->m_state == SoftBlocked || d->m_state == HardBlocked) {
0079         return;
0080     }
0081 
0082     setSoftBlock(1);
0083 }
0084 
0085 void Rfkill::unblock()
0086 {
0087     if (d->m_state == Unblocked) {
0088         return;
0089     }
0090 
0091     setSoftBlock(0);
0092 }
0093 
0094 void Rfkill::devReadyRead()
0095 {
0096     State oldState = d->m_state;
0097 
0098     updateRfkillDevices();
0099 
0100     if (d->m_state != oldState) {
0101         Q_EMIT stateChanged(d->m_state);
0102     }
0103 }
0104 
0105 void Rfkill::init()
0106 {
0107 #ifdef Q_OS_LINUX
0108     d->m_readFd = ::open("/dev/rfkill", O_RDONLY | O_CLOEXEC);
0109 
0110     if (d->m_readFd == -1) {
0111         qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for reading!";
0112         return;
0113     }
0114 
0115     if (::fcntl(d->m_readFd, F_SETFL, O_NONBLOCK) < 0) {
0116         ::close(d->m_readFd);
0117         d->m_readFd = -1;
0118         return;
0119     }
0120 
0121     updateRfkillDevices();
0122 
0123     QSocketNotifier *notifier = new QSocketNotifier(d->m_readFd, QSocketNotifier::Read, this);
0124     connect(notifier, &QSocketNotifier::activated, this, &Rfkill::devReadyRead);
0125 #endif
0126 }
0127 
0128 bool Rfkill::openForWriting()
0129 {
0130 #ifndef Q_OS_LINUX
0131     return false;
0132 #else
0133     if (d->m_writeFd != -1) {
0134         return true;
0135     }
0136 
0137     d->m_writeFd = ::open("/dev/rfkill", O_WRONLY | O_CLOEXEC);
0138 
0139     if (d->m_writeFd == -1) {
0140         qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for writing!";
0141         return false;
0142     }
0143 
0144     if (::fcntl(d->m_writeFd, F_SETFL, O_NONBLOCK) < 0) {
0145         ::close(d->m_writeFd);
0146         d->m_writeFd = -1;
0147         return false;
0148     }
0149 
0150     return true;
0151 #endif
0152 }
0153 
0154 #ifdef Q_OS_LINUX
0155 static Rfkill::State getState(const rfkill_event &event)
0156 {
0157     if (event.hard) {
0158         return Rfkill::HardBlocked;
0159     } else if (event.soft) {
0160         return Rfkill::SoftBlocked;
0161     }
0162     return Rfkill::Unblocked;
0163 }
0164 #endif
0165 
0166 void Rfkill::updateRfkillDevices()
0167 {
0168 #ifdef Q_OS_LINUX
0169     if (d->m_readFd == -1) {
0170         return;
0171     }
0172 
0173     rfkill_event event;
0174     while (::read(d->m_readFd, &event, sizeof(event)) == sizeof(event)) {
0175         if (event.type != RFKILL_TYPE_BLUETOOTH) {
0176             continue;
0177         }
0178 
0179         switch (event.op) {
0180         case RFKILL_OP_ADD:
0181         case RFKILL_OP_CHANGE:
0182             d->m_devices[event.idx] = getState(event);
0183             break;
0184 
0185         case RFKILL_OP_DEL:
0186             d->m_devices.remove(event.idx);
0187             break;
0188 
0189         case RFKILL_OP_CHANGE_ALL:
0190             for (auto it = d->m_devices.begin(); it != d->m_devices.end(); ++it) {
0191                 it.value() = getState(event);
0192             }
0193             break;
0194 
0195         default:
0196             break;
0197         }
0198     }
0199 
0200     // Update global state
0201     d->m_state = Unknown;
0202 
0203     for (State state : std::as_const(d->m_devices)) {
0204         Q_ASSERT(state != Unknown);
0205 
0206         if (d->m_state == Unknown) {
0207             d->m_state = state;
0208         } else if (state > d->m_state) {
0209             d->m_state = state;
0210         }
0211     }
0212 
0213     qCDebug(BLUEZQT) << "Rfkill global state changed:" << d->m_state;
0214 #endif
0215 }
0216 
0217 bool Rfkill::setSoftBlock(quint8 soft)
0218 {
0219 #ifndef Q_OS_LINUX
0220     Q_UNUSED(soft)
0221     return false;
0222 #else
0223     if (!openForWriting()) {
0224         return false;
0225     }
0226 
0227     rfkill_event event;
0228     ::memset(&event, 0, sizeof(event));
0229     event.op = RFKILL_OP_CHANGE_ALL;
0230     event.type = RFKILL_TYPE_BLUETOOTH;
0231     event.soft = soft;
0232 
0233     bool ret = ::write(d->m_writeFd, &event, sizeof(event)) == sizeof(event);
0234     qCDebug(BLUEZQT) << "Setting Rfkill soft block succeeded:" << ret;
0235     return ret;
0236 #endif
0237 }
0238 
0239 } // namespace BluezQt
0240 
0241 #include "moc_rfkill.cpp"