File indexing completed on 2024-12-01 12:29:51
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"