1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNfc module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qnearfieldtagtype2_p.h"
41#include "qnearfieldtarget_p.h"
42
43#include <QtCore/QVariant>
44#include <QtCore/QCoreApplication>
45#include <QtCore/QTime>
46
47#include <QtCore/QDebug>
48
49QT_BEGIN_NAMESPACE
50
51/*!
52 \class QNearFieldTagType2
53 \brief The QNearFieldTagType2 class provides an interface for communicating with an NFC Tag
54 Type 2 tag.
55
56 \ingroup connectivity-nfc
57 \inmodule QtNfc
58 \internal
59*/
60
61/*!
62 \fn Type QNearFieldTagType2::type() const
63 \reimp
64*/
65
66struct SectorSelectState {
67 int timerId; // id of timer used for passive ack
68 quint8 sector; // sector being selected
69};
70
71class QNearFieldTagType2Private
72{
73public:
74 QNearFieldTagType2Private() : m_currentSector(0) { }
75
76 QMap<QNearFieldTarget::RequestId, QByteArray> m_pendingInternalCommands;
77
78 quint8 m_currentSector;
79
80 QMap<QNearFieldTarget::RequestId, SectorSelectState> m_pendingSectorSelectCommands;
81};
82
83static QVariant decodeResponse(const QByteArray &command, const QByteArray &response)
84{
85 quint8 opcode = command.at(i: 0);
86
87 switch (opcode) {
88 case 0xa2: // WRITE
89 return quint8(response.at(i: 0)) == 0x0a;
90 case 0xc2: // SECTOR SELECT (Command Packet 1)
91 return quint8(response.at(i: 0)) == 0x0a;
92 }
93
94 return QVariant();
95}
96
97/*!
98 Constructs a new tag type 2 near field target with \a parent.
99*/
100QNearFieldTagType2::QNearFieldTagType2(QObject *parent)
101: QNearFieldTarget(parent), d_ptr(new QNearFieldTagType2Private)
102{
103}
104
105/*!
106 Destroys the tag type 2 near field target.
107*/
108QNearFieldTagType2::~QNearFieldTagType2()
109{
110 delete d_ptr;
111}
112
113/*!
114 \reimp
115*/
116bool QNearFieldTagType2::hasNdefMessage()
117{
118 qWarning() << Q_FUNC_INFO << "is unimplemeted";
119 return false;
120}
121
122/*!
123 \reimp
124*/
125QNearFieldTarget::RequestId QNearFieldTagType2::readNdefMessages()
126{
127 return RequestId();
128}
129
130/*!
131 \reimp
132*/
133QNearFieldTarget::RequestId QNearFieldTagType2::writeNdefMessages(const QList<QNdefMessage> &messages)
134{
135 Q_UNUSED(messages);
136
137 return RequestId();
138}
139
140/*!
141 Returns the NFC Tag Type 2 specification version number that the tag supports.
142*/
143quint8 QNearFieldTagType2::version()
144{
145 Q_D(QNearFieldTagType2);
146 if (d->m_currentSector != 0) {
147 RequestId id = selectSector(sector: 0);
148 if (!waitForRequestCompleted(id))
149 return 0;
150 }
151
152 RequestId id = readBlock(blockAddress: 0);
153 if (!waitForRequestCompleted(id))
154 return 0;
155
156 const QByteArray data = requestResponse(id).toByteArray();
157 return data.at(i: 13);
158}
159
160/*!
161 Returns the memory size in bytes of the tag.
162*/
163int QNearFieldTagType2::memorySize()
164{
165 Q_D(QNearFieldTagType2);
166 if (d->m_currentSector != 0) {
167 RequestId id = selectSector(sector: 0);
168 if (!waitForRequestCompleted(id))
169 return 0;
170 }
171
172 RequestId id = readBlock(blockAddress: 0);
173 if (!waitForRequestCompleted(id))
174 return 0;
175
176 const QByteArray data = requestResponse(id).toByteArray();
177 return 8 * quint8(data.at(i: 14));
178}
179
180/*!
181 Requests 16 bytes of data starting at \a blockAddress. Returns a request id which can be used
182 to track the completion status of the request.
183
184 Once the request completes successfully the response can be retrieved from the
185 requestResponse() function. The response of this request will be a QByteArray.
186
187 \sa requestCompleted(), waitForRequestCompleted()
188*/
189QNearFieldTarget::RequestId QNearFieldTagType2::readBlock(quint8 blockAddress)
190{
191 QByteArray command;
192 command.append(c: char(0x30)); // READ
193 command.append(c: char(blockAddress)); // Block address
194
195 return sendCommand(command);
196}
197
198/*!
199 Writes 4 bytes of \a data to the block at \a blockAddress. Returns a request id which can be
200 used to track the completion status of the request.
201
202 Once the request completes the response can be retrieved from the requestResponse() function.
203 The response of this request will be a boolean value, true for success; otherwise false.
204
205 Returns true on success; otherwise returns false.
206*/
207QNearFieldTarget::RequestId QNearFieldTagType2::writeBlock(quint8 blockAddress,
208 const QByteArray &data)
209{
210 if (data.length() != 4)
211 return RequestId();
212
213 QByteArray command;
214 command.append(c: char(0xa2)); // WRITE
215 command.append(c: char(blockAddress)); // Block address
216 command.append(a: data); // Data
217
218 RequestId id = sendCommand(command);
219
220 Q_D(QNearFieldTagType2);
221
222 d->m_pendingInternalCommands.insert(akey: id, avalue: command);
223
224 return id;
225}
226
227/*!
228 Selects the \a sector upon which subsequent readBlock() and writeBlock() operations will act.
229
230 Returns a request id which can be used to track the completion status of the request.
231
232 Once the request completes the response can be retrieved from the requestResponse() function.
233 The response of this request will be a boolean value, true for success; otherwise false.
234
235 \note this request has a passive acknowledgement mechanism. The operation is deemed successful
236 if no response is received within 1ms. It will therefore take a minimum of 1 millisecond for
237 the requestCompleted() signal to be emitted and calling waitForRequestCompleted() on the
238 returned request id may cause the current thread to block for up to 1 millisecond.
239*/
240QNearFieldTarget::RequestId QNearFieldTagType2::selectSector(quint8 sector)
241{
242 QByteArray command;
243 command.append(c: char(0xc2)); // SECTOR SELECT (Command Packet 1)
244 command.append(c: char(0xff));
245
246 RequestId id = sendCommand(command);
247
248 Q_D(QNearFieldTagType2);
249
250 d->m_pendingInternalCommands.insert(akey: id, avalue: command);
251
252 SectorSelectState state;
253 state.timerId = -1;
254 state.sector = sector;
255
256 d->m_pendingSectorSelectCommands.insert(akey: id, avalue: state);
257
258 return id;
259}
260
261/*!
262 \reimp
263*/
264bool QNearFieldTagType2::handleResponse(const QNearFieldTarget::RequestId &id,
265 const QByteArray &response)
266{
267 Q_D(QNearFieldTagType2);
268
269 if (d->m_pendingInternalCommands.contains(akey: id)) {
270 const QByteArray command = d->m_pendingInternalCommands.take(akey: id);
271
272 QVariant decodedResponse = decodeResponse(command, response);
273 if (quint8(command.at(i: 0)) == 0xc2 && decodedResponse.toBool()) {
274 // SECTOR SELECT (Command Packet 2)
275 SectorSelectState &state = d->m_pendingSectorSelectCommands[id];
276
277 QByteArray packet2;
278 packet2.append(c: char(state.sector));
279 packet2.append(a: QByteArray(3, 0x00));
280
281 sendCommand(command: packet2);
282
283 state.timerId = startTimer(interval: 1);
284 } else {
285 setResponseForRequest(id, response: decodedResponse);
286 }
287
288 return true;
289 } else if (d->m_pendingSectorSelectCommands.contains(akey: id)) {
290 if (!response.isEmpty()) {
291 d->m_pendingSectorSelectCommands.remove(akey: id);
292 setResponseForRequest(id, response: false);
293
294 return true;
295 }
296 }
297
298 return QNearFieldTarget::handleResponse(id, response);
299}
300
301/*!
302 \internal
303*/
304void QNearFieldTagType2::timerEvent(QTimerEvent *event)
305{
306 Q_D(QNearFieldTagType2);
307
308 killTimer(id: event->timerId());
309
310 for (auto i = d->m_pendingSectorSelectCommands.begin(), end = d->m_pendingSectorSelectCommands.end(); i != end; ++i) {
311
312 SectorSelectState &state = i.value();
313
314 if (state.timerId == event->timerId()) {
315 d->m_currentSector = state.sector;
316
317 setResponseForRequest(id: i.key(), response: true);
318
319 d->m_pendingSectorSelectCommands.erase(it: i);
320 break;
321 }
322 }
323}
324
325QT_END_NAMESPACE
326

source code of qtconnectivity/src/nfc/qnearfieldtagtype2.cpp