1/*
2 * BluezQt - Asynchronous Bluez wrapper library
3 *
4 * SPDX-FileCopyrightText: 2015 David Rosca <nowrep@gmail.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9#include "rfkill.h"
10#include "rfkill_p.h"
11
12#include "debug.h"
13
14#ifdef Q_OS_LINUX
15#include <fcntl.h>
16#include <stdint.h>
17#include <string.h>
18#include <unistd.h>
19#endif
20
21#include <QSocketNotifier>
22
23namespace BluezQt
24{
25#ifdef Q_OS_LINUX
26enum rfkill_type {
27 RFKILL_TYPE_ALL = 0,
28 RFKILL_TYPE_WLAN,
29 RFKILL_TYPE_BLUETOOTH,
30 RFKILL_TYPE_UWB,
31 RFKILL_TYPE_WIMAX,
32 RFKILL_TYPE_WWAN,
33};
34
35enum rfkill_operation {
36 RFKILL_OP_ADD = 0,
37 RFKILL_OP_DEL,
38 RFKILL_OP_CHANGE,
39 RFKILL_OP_CHANGE_ALL,
40};
41
42struct rfkill_event {
43 quint32 idx;
44 quint8 type;
45 quint8 op;
46 quint8 soft;
47 quint8 hard;
48};
49#endif
50
51Rfkill::Rfkill(QObject *parent)
52 : QObject(parent)
53 , d(new RfkillPrivate)
54{
55 init();
56}
57
58Rfkill::~Rfkill()
59{
60#ifdef Q_OS_LINUX
61 if (d->m_readFd != -1) {
62 ::close(fd: d->m_readFd);
63 }
64
65 if (d->m_writeFd != -1) {
66 ::close(fd: d->m_writeFd);
67 }
68#endif
69}
70
71Rfkill::State Rfkill::state() const
72{
73 return d->m_state;
74}
75
76void Rfkill::block()
77{
78 if (d->m_state == SoftBlocked || d->m_state == HardBlocked) {
79 return;
80 }
81
82 setSoftBlock(1);
83}
84
85void Rfkill::unblock()
86{
87 if (d->m_state == Unblocked) {
88 return;
89 }
90
91 setSoftBlock(0);
92}
93
94void Rfkill::devReadyRead()
95{
96 State oldState = d->m_state;
97
98 updateRfkillDevices();
99
100 if (d->m_state != oldState) {
101 Q_EMIT stateChanged(state: d->m_state);
102 }
103}
104
105void Rfkill::init()
106{
107#ifdef Q_OS_LINUX
108 d->m_readFd = ::open(file: "/dev/rfkill", O_RDONLY | O_CLOEXEC);
109
110 if (d->m_readFd == -1) {
111 qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for reading!";
112 return;
113 }
114
115 if (::fcntl(fd: d->m_readFd, F_SETFL, O_NONBLOCK) < 0) {
116 ::close(fd: d->m_readFd);
117 d->m_readFd = -1;
118 return;
119 }
120
121 updateRfkillDevices();
122
123 QSocketNotifier *notifier = new QSocketNotifier(d->m_readFd, QSocketNotifier::Read, this);
124 connect(sender: notifier, signal: &QSocketNotifier::activated, context: this, slot: &Rfkill::devReadyRead);
125#endif
126}
127
128bool Rfkill::openForWriting()
129{
130#ifndef Q_OS_LINUX
131 return false;
132#else
133 if (d->m_writeFd != -1) {
134 return true;
135 }
136
137 d->m_writeFd = ::open(file: "/dev/rfkill", O_WRONLY | O_CLOEXEC);
138
139 if (d->m_writeFd == -1) {
140 qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for writing!";
141 return false;
142 }
143
144 if (::fcntl(fd: d->m_writeFd, F_SETFL, O_NONBLOCK) < 0) {
145 ::close(fd: d->m_writeFd);
146 d->m_writeFd = -1;
147 return false;
148 }
149
150 return true;
151#endif
152}
153
154#ifdef Q_OS_LINUX
155static Rfkill::State getState(const rfkill_event &event)
156{
157 if (event.hard) {
158 return Rfkill::HardBlocked;
159 } else if (event.soft) {
160 return Rfkill::SoftBlocked;
161 }
162 return Rfkill::Unblocked;
163}
164#endif
165
166void Rfkill::updateRfkillDevices()
167{
168#ifdef Q_OS_LINUX
169 if (d->m_readFd == -1) {
170 return;
171 }
172
173 rfkill_event event;
174 while (::read(fd: d->m_readFd, buf: &event, nbytes: sizeof(event)) == sizeof(event)) {
175 if (event.type != RFKILL_TYPE_BLUETOOTH) {
176 continue;
177 }
178
179 switch (event.op) {
180 case RFKILL_OP_ADD:
181 case RFKILL_OP_CHANGE:
182 d->m_devices[event.idx] = getState(event);
183 break;
184
185 case RFKILL_OP_DEL:
186 d->m_devices.remove(key: event.idx);
187 break;
188
189 case RFKILL_OP_CHANGE_ALL:
190 for (auto it = d->m_devices.begin(); it != d->m_devices.end(); ++it) {
191 it.value() = getState(event);
192 }
193 break;
194
195 default:
196 break;
197 }
198 }
199
200 // Update global state
201 d->m_state = Unknown;
202
203 for (State state : std::as_const(t&: d->m_devices)) {
204 Q_ASSERT(state != Unknown);
205
206 if (d->m_state == Unknown) {
207 d->m_state = state;
208 } else if (state > d->m_state) {
209 d->m_state = state;
210 }
211 }
212
213 qCDebug(BLUEZQT) << "Rfkill global state changed:" << d->m_state;
214#endif
215}
216
217bool Rfkill::setSoftBlock(quint8 soft)
218{
219#ifndef Q_OS_LINUX
220 Q_UNUSED(soft)
221 return false;
222#else
223 if (!openForWriting()) {
224 return false;
225 }
226
227 rfkill_event event;
228 ::memset(s: &event, c: 0, n: sizeof(event));
229 event.op = RFKILL_OP_CHANGE_ALL;
230 event.type = RFKILL_TYPE_BLUETOOTH;
231 event.soft = soft;
232
233 bool ret = ::write(fd: d->m_writeFd, buf: &event, n: sizeof(event)) == sizeof(event);
234 qCDebug(BLUEZQT) << "Setting Rfkill soft block succeeded:" << ret;
235 return ret;
236#endif
237}
238
239} // namespace BluezQt
240
241#include "moc_rfkill.cpp"
242

source code of bluez-qt/src/rfkill.cpp