BluezQt

rfkill.cpp
1 /*
2  * BluezQt - Asynchronous Bluez wrapper library
3  *
4  * SPDX-FileCopyrightText: 2015 David Rosca <[email protected]>
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 
23 namespace BluezQt
24 {
25 #ifdef Q_OS_LINUX
26 enum 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 
35 enum rfkill_operation {
36  RFKILL_OP_ADD = 0,
37  RFKILL_OP_DEL,
38  RFKILL_OP_CHANGE,
39  RFKILL_OP_CHANGE_ALL,
40 };
41 
42 struct rfkill_event {
43  quint32 idx;
44  quint8 type;
45  quint8 op;
46  quint8 soft;
47  quint8 hard;
48 };
49 #endif
50 
51 Rfkill::Rfkill(QObject *parent)
52  : QObject(parent)
53  , d(new RfkillPrivate)
54 {
55  init();
56 }
57 
58 Rfkill::~Rfkill()
59 {
60 #ifdef Q_OS_LINUX
61  if (d->m_readFd != -1) {
62  ::close(d->m_readFd);
63  }
64 
65  if (d->m_writeFd != -1) {
66  ::close(d->m_writeFd);
67  }
68 #endif
69 }
70 
71 Rfkill::State Rfkill::state() const
72 {
73  return d->m_state;
74 }
75 
76 bool Rfkill::block()
77 {
78  if (d->m_state == SoftBlocked || d->m_state == HardBlocked) {
79  return true;
80  }
81 
82  if (d->m_state != Unblocked) {
83  return false;
84  }
85 
86  return setSoftBlock(1);
87 }
88 
89 bool Rfkill::unblock()
90 {
91  if (d->m_state == Unblocked) {
92  return true;
93  }
94 
95  if (d->m_state != SoftBlocked) {
96  return false;
97  }
98 
99  return setSoftBlock(0);
100 }
101 
102 void Rfkill::devReadyRead()
103 {
104  State oldState = d->m_state;
105 
106  updateRfkillDevices();
107 
108  if (d->m_state != oldState) {
109  Q_EMIT stateChanged(d->m_state);
110  }
111 }
112 
113 void Rfkill::init()
114 {
115 #ifdef Q_OS_LINUX
116  d->m_readFd = ::open("/dev/rfkill", O_RDONLY | O_CLOEXEC);
117 
118  if (d->m_readFd == -1) {
119  qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for reading!";
120  return;
121  }
122 
123  if (::fcntl(d->m_readFd, F_SETFL, O_NONBLOCK) < 0) {
124  ::close(d->m_readFd);
125  d->m_readFd = -1;
126  return;
127  }
128 
129  updateRfkillDevices();
130 
132  connect(notifier, &QSocketNotifier::activated, this, &Rfkill::devReadyRead);
133 #endif
134 }
135 
136 bool Rfkill::openForWriting()
137 {
138 #ifndef Q_OS_LINUX
139  return false;
140 #else
141  if (d->m_writeFd != -1) {
142  return true;
143  }
144 
145  d->m_writeFd = ::open("/dev/rfkill", O_WRONLY | O_CLOEXEC);
146 
147  if (d->m_writeFd == -1) {
148  qCWarning(BLUEZQT) << "Cannot open /dev/rfkill for writing!";
149  return false;
150  }
151 
152  if (::fcntl(d->m_writeFd, F_SETFL, O_NONBLOCK) < 0) {
153  ::close(d->m_writeFd);
154  d->m_writeFd = -1;
155  return false;
156  }
157 
158  return true;
159 #endif
160 }
161 
162 #ifdef Q_OS_LINUX
163 static Rfkill::State getState(const rfkill_event &event)
164 {
165  if (event.hard) {
166  return Rfkill::HardBlocked;
167  } else if (event.soft) {
168  return Rfkill::SoftBlocked;
169  }
170  return Rfkill::Unblocked;
171 }
172 #endif
173 
174 void Rfkill::updateRfkillDevices()
175 {
176 #ifdef Q_OS_LINUX
177  if (d->m_readFd == -1) {
178  return;
179  }
180 
181  rfkill_event event;
182  while (::read(d->m_readFd, &event, sizeof(event)) == sizeof(event)) {
183  if (event.type != RFKILL_TYPE_BLUETOOTH) {
184  continue;
185  }
186 
187  switch (event.op) {
188  case RFKILL_OP_ADD:
189  case RFKILL_OP_CHANGE:
190  d->m_devices[event.idx] = getState(event);
191  break;
192 
193  case RFKILL_OP_DEL:
194  d->m_devices.remove(event.idx);
195  break;
196 
197  case RFKILL_OP_CHANGE_ALL:
198  for (auto it = d->m_devices.begin(); it != d->m_devices.end(); ++it) {
199  it.value() = getState(event);
200  }
201  break;
202 
203  default:
204  break;
205  }
206  }
207 
208  // Update global state
209  d->m_state = Unknown;
210 
211  for (State state : std::as_const(d->m_devices)) {
212  Q_ASSERT(state != Unknown);
213 
214  if (d->m_state == Unknown) {
215  d->m_state = state;
216  } else if (state > d->m_state) {
217  d->m_state = state;
218  }
219  }
220 
221  qCDebug(BLUEZQT) << "Rfkill global state changed:" << d->m_state;
222 #endif
223 }
224 
225 bool Rfkill::setSoftBlock(quint8 soft)
226 {
227 #ifndef Q_OS_LINUX
228  Q_UNUSED(soft)
229  return false;
230 #else
231  if (!openForWriting()) {
232  return false;
233  }
234 
235  rfkill_event event;
236  ::memset(&event, 0, sizeof(event));
237  event.op = RFKILL_OP_CHANGE_ALL;
238  event.type = RFKILL_TYPE_BLUETOOTH;
239  event.soft = soft;
240 
241  bool ret = ::write(d->m_writeFd, &event, sizeof(event)) == sizeof(event);
242  qCDebug(BLUEZQT) << "Setting Rfkill soft block succeeded:" << ret;
243  return ret;
244 #endif
245 }
246 
247 } // namespace BluezQt
QCA_EXPORT void init()
PHONON_EXPORT Notifier * notifier()
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QVariant read(const QByteArray &data, int versionOverride=0)
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sun Sep 25 2022 04:19:10 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.