File indexing completed on 2025-01-19 06:44:39
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"