File indexing completed on 2024-04-28 15:17:56

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 bool Rfkill::block()
0077 {
0078     if (d->m_state == SoftBlocked || d->m_state == HardBlocked) {
0079         return true;
0080     }
0081 
0082     if (d->m_state != Unblocked) {
0083         return false;
0084     }
0085 
0086     return setSoftBlock(1);
0087 }
0088 
0089 bool Rfkill::unblock()
0090 {
0091     if (d->m_state == Unblocked) {
0092         return true;
0093     }
0094 
0095     if (d->m_state != SoftBlocked) {
0096         return false;
0097     }
0098 
0099     return setSoftBlock(0);
0100 }
0101 
0102 void Rfkill::devReadyRead()
0103 {
0104     State oldState = d->m_state;
0105 
0106     updateRfkillDevices();
0107 
0108     if (d->m_state != oldState) {
0109         Q_EMIT stateChanged(d->m_state);
0110     }
0111 }
0112 
0113 void Rfkill::init()
0114 {
0115 #ifdef Q_OS_LINUX
0116     d->m_readFd = ::open("/dev/rfkill", O_RDONLY | O_CLOEXEC);
0117 
0118     if (d->m_readFd == -1) {
0119         qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for reading!";
0120         return;
0121     }
0122 
0123     if (::fcntl(d->m_readFd, F_SETFL, O_NONBLOCK) < 0) {
0124         ::close(d->m_readFd);
0125         d->m_readFd = -1;
0126         return;
0127     }
0128 
0129     updateRfkillDevices();
0130 
0131     QSocketNotifier *notifier = new QSocketNotifier(d->m_readFd, QSocketNotifier::Read, this);
0132     connect(notifier, &QSocketNotifier::activated, this, &Rfkill::devReadyRead);
0133 #endif
0134 }
0135 
0136 bool Rfkill::openForWriting()
0137 {
0138 #ifndef Q_OS_LINUX
0139     return false;
0140 #else
0141     if (d->m_writeFd != -1) {
0142         return true;
0143     }
0144 
0145     d->m_writeFd = ::open("/dev/rfkill", O_WRONLY | O_CLOEXEC);
0146 
0147     if (d->m_writeFd == -1) {
0148         qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for writing!";
0149         return false;
0150     }
0151 
0152     if (::fcntl(d->m_writeFd, F_SETFL, O_NONBLOCK) < 0) {
0153         ::close(d->m_writeFd);
0154         d->m_writeFd = -1;
0155         return false;
0156     }
0157 
0158     return true;
0159 #endif
0160 }
0161 
0162 #ifdef Q_OS_LINUX
0163 static Rfkill::State getState(const rfkill_event &event)
0164 {
0165     if (event.hard) {
0166         return Rfkill::HardBlocked;
0167     } else if (event.soft) {
0168         return Rfkill::SoftBlocked;
0169     }
0170     return Rfkill::Unblocked;
0171 }
0172 #endif
0173 
0174 void Rfkill::updateRfkillDevices()
0175 {
0176 #ifdef Q_OS_LINUX
0177     if (d->m_readFd == -1) {
0178         return;
0179     }
0180 
0181     rfkill_event event;
0182     while (::read(d->m_readFd, &event, sizeof(event)) == sizeof(event)) {
0183         if (event.type != RFKILL_TYPE_BLUETOOTH) {
0184             continue;
0185         }
0186 
0187         switch (event.op) {
0188         case RFKILL_OP_ADD:
0189         case RFKILL_OP_CHANGE:
0190             d->m_devices[event.idx] = getState(event);
0191             break;
0192 
0193         case RFKILL_OP_DEL:
0194             d->m_devices.remove(event.idx);
0195             break;
0196 
0197         case RFKILL_OP_CHANGE_ALL:
0198             for (auto it = d->m_devices.begin(); it != d->m_devices.end(); ++it) {
0199                 it.value() = getState(event);
0200             }
0201             break;
0202 
0203         default:
0204             break;
0205         }
0206     }
0207 
0208     // Update global state
0209     d->m_state = Unknown;
0210 
0211     for (State state : std::as_const(d->m_devices)) {
0212         Q_ASSERT(state != Unknown);
0213 
0214         if (d->m_state == Unknown) {
0215             d->m_state = state;
0216         } else if (state > d->m_state) {
0217             d->m_state = state;
0218         }
0219     }
0220 
0221     qCDebug(BLUEZQT) << "Rfkill global state changed:" << d->m_state;
0222 #endif
0223 }
0224 
0225 bool Rfkill::setSoftBlock(quint8 soft)
0226 {
0227 #ifndef Q_OS_LINUX
0228     Q_UNUSED(soft)
0229     return false;
0230 #else
0231     if (!openForWriting()) {
0232         return false;
0233     }
0234 
0235     rfkill_event event;
0236     ::memset(&event, 0, sizeof(event));
0237     event.op = RFKILL_OP_CHANGE_ALL;
0238     event.type = RFKILL_TYPE_BLUETOOTH;
0239     event.soft = soft;
0240 
0241     bool ret = ::write(d->m_writeFd, &event, sizeof(event)) == sizeof(event);
0242     qCDebug(BLUEZQT) << "Setting Rfkill soft block succeeded:" << ret;
0243     return ret;
0244 #endif
0245 }
0246 
0247 } // namespace BluezQt
0248 
0249 #include "moc_rfkill.cpp"