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 "qnearfieldtagtype1_p.h" |
41 | #include "qnearfieldtarget_p.h" |
42 | #include "qndefmessage.h" |
43 | #include "qtlv_p.h" |
44 | |
45 | #include <QtCore/QByteArray> |
46 | #include <QtCore/QVariant> |
47 | |
48 | #include <QtCore/QDebug> |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | /*! |
53 | \class QNearFieldTagType1 |
54 | \brief The QNearFieldTagType1 class provides an interface for communicating with an NFC Tag |
55 | Type 1 tag. |
56 | |
57 | \ingroup connectivity-nfc |
58 | \inmodule QtNfc |
59 | \internal |
60 | */ |
61 | |
62 | /*! |
63 | \enum QNearFieldTagType1::WriteMode |
64 | \brief This enum describes the write modes that are supported. |
65 | |
66 | \value EraseAndWrite The memory is erased before the new value is written. |
67 | \value WriteOnly The memory is not erased before the new value is written. The effect of |
68 | this mode is that the final value store is the bitwise or of the data |
69 | to be written and the original data value. |
70 | */ |
71 | |
72 | /*! |
73 | \fn Type QNearFieldTagType1::type() const |
74 | \reimp |
75 | */ |
76 | |
77 | class QNearFieldTagType1Private |
78 | { |
79 | Q_DECLARE_PUBLIC(QNearFieldTagType1) |
80 | |
81 | public: |
82 | QNearFieldTagType1Private(QNearFieldTagType1 *q) |
83 | : q_ptr(q), m_readNdefMessageState(NotReadingNdefMessage), |
84 | m_tlvReader(0), |
85 | m_writeNdefMessageState(NotWritingNdefMessage), |
86 | m_tlvWriter(0) |
87 | { } |
88 | |
89 | QNearFieldTagType1 *q_ptr; |
90 | |
91 | QMap<QNearFieldTarget::RequestId, QByteArray> m_pendingInternalCommands; |
92 | |
93 | enum ReadNdefMessageState { |
94 | NotReadingNdefMessage, |
95 | NdefReadCheckingIdentification, |
96 | NdefReadCheckingNdefMagicNumber, |
97 | NdefReadReadingTlv |
98 | }; |
99 | |
100 | void progressToNextNdefReadMessageState(); |
101 | ReadNdefMessageState m_readNdefMessageState; |
102 | QNearFieldTarget::RequestId m_readNdefRequestId; |
103 | |
104 | QTlvReader *m_tlvReader; |
105 | QNearFieldTarget::RequestId m_nextExpectedRequestId; |
106 | |
107 | enum WriteNdefMessageState { |
108 | NotWritingNdefMessage, |
109 | NdefWriteCheckingIdentification, |
110 | NdefWriteCheckingNdefMagicNumber, |
111 | NdefWriteReadingTlv, |
112 | NdefWriteWritingTlv, |
113 | NdefWriteWritingTlvFlush |
114 | }; |
115 | |
116 | void progressToNextNdefWriteMessageState(); |
117 | WriteNdefMessageState m_writeNdefMessageState; |
118 | QNearFieldTarget::RequestId m_writeNdefRequestId; |
119 | QList<QNdefMessage> m_ndefWriteMessages; |
120 | |
121 | QTlvWriter *m_tlvWriter; |
122 | |
123 | typedef QPair<quint8, QByteArray> Tlv; |
124 | QList<Tlv> m_tlvs; |
125 | }; |
126 | |
127 | void QNearFieldTagType1Private::progressToNextNdefReadMessageState() |
128 | { |
129 | Q_Q(QNearFieldTagType1); |
130 | |
131 | switch (m_readNdefMessageState) { |
132 | case NotReadingNdefMessage: |
133 | m_readNdefMessageState = NdefReadCheckingIdentification; |
134 | m_nextExpectedRequestId = q->readIdentification(); |
135 | break; |
136 | case NdefReadCheckingIdentification: { |
137 | const QByteArray data = q->requestResponse(id: m_nextExpectedRequestId).toByteArray(); |
138 | |
139 | if (data.isEmpty()) { |
140 | m_readNdefMessageState = NotReadingNdefMessage; |
141 | m_nextExpectedRequestId = QNearFieldTarget::RequestId(); |
142 | emit q->error(error: QNearFieldTarget::NdefReadError, id: m_readNdefRequestId); |
143 | m_readNdefRequestId = QNearFieldTarget::RequestId(); |
144 | break; |
145 | } |
146 | |
147 | quint8 hr0 = data.at(i: 0); |
148 | |
149 | // Check if target is a NFC TagType1 tag |
150 | if (!(hr0 & 0x10)) { |
151 | m_readNdefMessageState = NotReadingNdefMessage; |
152 | m_nextExpectedRequestId = QNearFieldTarget::RequestId(); |
153 | emit q->error(error: QNearFieldTarget::NdefReadError, id: m_readNdefRequestId); |
154 | m_readNdefRequestId = QNearFieldTarget::RequestId(); |
155 | break; |
156 | } |
157 | |
158 | m_readNdefMessageState = NdefReadCheckingNdefMagicNumber; |
159 | m_nextExpectedRequestId = q->readByte(address: 8); |
160 | break; |
161 | } |
162 | case NdefReadCheckingNdefMagicNumber: { |
163 | quint8 ndefMagicNumber = q->requestResponse(id: m_nextExpectedRequestId).toUInt(); |
164 | m_nextExpectedRequestId = QNearFieldTarget::RequestId(); |
165 | |
166 | if (ndefMagicNumber != 0xe1) { |
167 | m_readNdefMessageState = NotReadingNdefMessage; |
168 | emit q->error(error: QNearFieldTarget::NdefReadError, id: m_readNdefRequestId); |
169 | m_readNdefRequestId = QNearFieldTarget::RequestId(); |
170 | break; |
171 | } |
172 | |
173 | m_readNdefMessageState = NdefReadReadingTlv; |
174 | delete m_tlvReader; |
175 | m_tlvReader = new QTlvReader(q); |
176 | |
177 | Q_FALLTHROUGH(); // fall through |
178 | } |
179 | case NdefReadReadingTlv: |
180 | Q_ASSERT(m_tlvReader); |
181 | while (!m_tlvReader->atEnd()) { |
182 | if (!m_tlvReader->readNext()) |
183 | break; |
184 | |
185 | // NDEF Message TLV |
186 | if (m_tlvReader->tag() == 0x03) { |
187 | Q_Q(QNearFieldTagType1); |
188 | |
189 | emit q->ndefMessageRead(message: QNdefMessage::fromByteArray(message: m_tlvReader->data())); |
190 | } |
191 | } |
192 | |
193 | m_nextExpectedRequestId = m_tlvReader->requestId(); |
194 | if (!m_nextExpectedRequestId.isValid()) { |
195 | delete m_tlvReader; |
196 | m_tlvReader = 0; |
197 | m_readNdefMessageState = NotReadingNdefMessage; |
198 | emit q->requestCompleted(id: m_readNdefRequestId); |
199 | m_readNdefRequestId = QNearFieldTarget::RequestId(); |
200 | } |
201 | break; |
202 | } |
203 | } |
204 | |
205 | void QNearFieldTagType1Private::progressToNextNdefWriteMessageState() |
206 | { |
207 | Q_Q(QNearFieldTagType1); |
208 | |
209 | switch (m_writeNdefMessageState) { |
210 | case NotWritingNdefMessage: |
211 | m_writeNdefMessageState = NdefWriteCheckingIdentification; |
212 | m_nextExpectedRequestId = q->readIdentification(); |
213 | break; |
214 | case NdefWriteCheckingIdentification: { |
215 | const QByteArray data = q->requestResponse(id: m_nextExpectedRequestId).toByteArray(); |
216 | |
217 | if (data.isEmpty()) { |
218 | m_writeNdefMessageState = NotWritingNdefMessage; |
219 | m_nextExpectedRequestId = QNearFieldTarget::RequestId(); |
220 | emit q->error(error: QNearFieldTarget::NdefWriteError, id: m_writeNdefRequestId); |
221 | m_writeNdefRequestId = QNearFieldTarget::RequestId(); |
222 | break; |
223 | } |
224 | |
225 | quint8 hr0 = data.at(i: 0); |
226 | |
227 | // Check if target is a NFC TagType1 tag |
228 | if (!(hr0 & 0x10)) { |
229 | m_writeNdefMessageState = NotWritingNdefMessage; |
230 | m_nextExpectedRequestId = QNearFieldTarget::RequestId(); |
231 | emit q->error(error: QNearFieldTarget::NdefWriteError, id: m_writeNdefRequestId); |
232 | m_writeNdefRequestId = QNearFieldTarget::RequestId(); |
233 | break; |
234 | } |
235 | |
236 | m_writeNdefMessageState = NdefWriteCheckingNdefMagicNumber; |
237 | m_nextExpectedRequestId = q->readByte(address: 8); |
238 | break; |
239 | } |
240 | case NdefWriteCheckingNdefMagicNumber: { |
241 | quint8 ndefMagicNumber = q->requestResponse(id: m_nextExpectedRequestId).toUInt(); |
242 | m_nextExpectedRequestId = QNearFieldTarget::RequestId(); |
243 | |
244 | if (ndefMagicNumber != 0xe1) { |
245 | m_writeNdefMessageState = NotWritingNdefMessage; |
246 | emit q->error(error: QNearFieldTarget::NdefWriteError, id: m_writeNdefRequestId); |
247 | m_writeNdefRequestId = QNearFieldTarget::RequestId(); |
248 | break; |
249 | } |
250 | |
251 | m_writeNdefMessageState = NdefWriteReadingTlv; |
252 | delete m_tlvReader; |
253 | m_tlvReader = new QTlvReader(q); |
254 | |
255 | Q_FALLTHROUGH(); // fall through |
256 | } |
257 | case NdefWriteReadingTlv: |
258 | Q_ASSERT(m_tlvReader); |
259 | while (!m_tlvReader->atEnd()) { |
260 | if (!m_tlvReader->readNext()) |
261 | break; |
262 | |
263 | quint8 tag = m_tlvReader->tag(); |
264 | if (tag == 0x01 || tag == 0x02 || tag == 0xfd) |
265 | m_tlvs.append(t: qMakePair(x: tag, y: m_tlvReader->data())); |
266 | } |
267 | |
268 | m_nextExpectedRequestId = m_tlvReader->requestId(); |
269 | if (m_nextExpectedRequestId.isValid()) |
270 | break; |
271 | |
272 | delete m_tlvReader; |
273 | m_tlvReader = 0; |
274 | m_writeNdefMessageState = NdefWriteWritingTlv; |
275 | |
276 | // fall through |
277 | case NdefWriteWritingTlv: |
278 | delete m_tlvWriter; |
279 | m_tlvWriter = new QTlvWriter(q); |
280 | |
281 | // write old TLVs |
282 | for (const Tlv &tlv : qAsConst(t&: m_tlvs)) |
283 | m_tlvWriter->writeTlv(tag: tlv.first, data: tlv.second); |
284 | |
285 | // write new NDEF message TLVs |
286 | for (const QNdefMessage &message : qAsConst(t&: m_ndefWriteMessages)) |
287 | m_tlvWriter->writeTlv(tag: 0x03, data: message.toByteArray()); |
288 | |
289 | // write terminator TLV |
290 | m_tlvWriter->writeTlv(tag: 0xfe); |
291 | |
292 | m_writeNdefMessageState = NdefWriteWritingTlvFlush; |
293 | |
294 | // fall through |
295 | case NdefWriteWritingTlvFlush: |
296 | // flush the writer |
297 | Q_ASSERT(m_tlvWriter); |
298 | if (m_tlvWriter->process(all: true)) { |
299 | m_nextExpectedRequestId = QNearFieldTarget::RequestId(); |
300 | m_writeNdefMessageState = NotWritingNdefMessage; |
301 | delete m_tlvWriter; |
302 | m_tlvWriter = 0; |
303 | emit q->ndefMessagesWritten(); |
304 | emit q->requestCompleted(id: m_writeNdefRequestId); |
305 | m_writeNdefRequestId = QNearFieldTarget::RequestId(); |
306 | } else { |
307 | m_nextExpectedRequestId = m_tlvWriter->requestId(); |
308 | if (!m_nextExpectedRequestId.isValid()) { |
309 | m_writeNdefMessageState = NotWritingNdefMessage; |
310 | delete m_tlvWriter; |
311 | m_tlvWriter = 0; |
312 | emit q->error(error: QNearFieldTarget::NdefWriteError, id: m_writeNdefRequestId); |
313 | m_writeNdefRequestId = QNearFieldTarget::RequestId(); |
314 | } |
315 | } |
316 | break; |
317 | } |
318 | } |
319 | |
320 | static QVariant decodeResponse(const QByteArray &command, const QByteArray &response) |
321 | { |
322 | switch (command.at(i: 0)) { |
323 | case 0x01: // READ |
324 | if (command.at(i: 1) == response.at(i: 0)) |
325 | return quint8(response.at(i: 1)); |
326 | break; |
327 | case 0x53: { // WRITE-E |
328 | quint8 address = command.at(i: 1); |
329 | quint8 data = command.at(i: 2); |
330 | quint8 writeAddress = response.at(i: 0); |
331 | quint8 writeData = response.at(i: 1); |
332 | |
333 | return ((writeAddress == address) && (writeData == data)); |
334 | } |
335 | case 0x1a: { // WRITE-NE |
336 | quint8 address = command.at(i: 1); |
337 | quint8 data = command.at(i: 2); |
338 | quint8 writeAddress = response.at(i: 0); |
339 | quint8 writeData = response.at(i: 1); |
340 | |
341 | return ((writeAddress == address) && ((writeData & data) == data)); |
342 | } |
343 | case 0x10: { // RSEG |
344 | quint8 segmentAddress = quint8(command.at(i: 1)) >> 4; |
345 | quint8 readSegmentAddress = quint8(response.at(i: 0)) >> 4; |
346 | if (readSegmentAddress == segmentAddress) |
347 | return response.mid(index: 1); |
348 | break; |
349 | } |
350 | case 0x02: { // READ8 |
351 | quint8 blockAddress = command.at(i: 1); |
352 | quint8 readBlockAddress = response.at(i: 0); |
353 | if (readBlockAddress == blockAddress) |
354 | return response.mid(index: 1); |
355 | break; |
356 | } |
357 | case 0x54: { // WRITE-E8 |
358 | quint8 blockAddress = command.at(i: 1); |
359 | QByteArray data = command.mid(index: 2, len: 8); |
360 | quint8 writeBlockAddress = response.at(i: 0); |
361 | QByteArray writeData = response.mid(index: 1); |
362 | |
363 | return ((writeBlockAddress == blockAddress) && (writeData == data)); |
364 | } |
365 | case 0x1b: { // WRITE-NE8 |
366 | quint8 blockAddress = command.at(i: 1); |
367 | QByteArray data = command.mid(index: 2, len: 8); |
368 | quint8 writeBlockAddress = response.at(i: 0); |
369 | QByteArray writeData = response.mid(index: 1); |
370 | |
371 | if (writeBlockAddress != blockAddress) |
372 | return false; |
373 | |
374 | for (int i = 0; i < writeData.length(); ++i) { |
375 | if ((writeData.at(i) & data.at(i)) != data.at(i)) |
376 | return false; |
377 | } |
378 | |
379 | return true; |
380 | } |
381 | } |
382 | |
383 | return QVariant(); |
384 | } |
385 | |
386 | /*! |
387 | Constructs a new tag type 1 near field target with \a parent. |
388 | */ |
389 | QNearFieldTagType1::QNearFieldTagType1(QObject *parent) |
390 | : QNearFieldTarget(parent), d_ptr(new QNearFieldTagType1Private(this)) |
391 | { |
392 | } |
393 | |
394 | /*! |
395 | Destroys the tag type 1 near field target. |
396 | */ |
397 | QNearFieldTagType1::~QNearFieldTagType1() |
398 | { |
399 | delete d_ptr; |
400 | } |
401 | |
402 | /*! |
403 | \reimp |
404 | */ |
405 | bool QNearFieldTagType1::hasNdefMessage() |
406 | { |
407 | RequestId id = readAll(); |
408 | if (!waitForRequestCompleted(id)) |
409 | return false; |
410 | |
411 | const QByteArray data = requestResponse(id).toByteArray(); |
412 | |
413 | if (data.isEmpty()) |
414 | return false; |
415 | |
416 | quint8 hr0 = data.at(i: 0); |
417 | |
418 | // Check if target is a NFC TagType1 tag |
419 | if (!(hr0 & 0x10)) |
420 | return false; |
421 | |
422 | // Check if NDEF Message Magic number is present |
423 | quint8 nmn = data.at(i: 10); |
424 | if (nmn != 0xe1) |
425 | return false; |
426 | |
427 | // Check if TLV contains NDEF Message |
428 | return true; |
429 | } |
430 | |
431 | /*! |
432 | \reimp |
433 | */ |
434 | QNearFieldTarget::RequestId QNearFieldTagType1::readNdefMessages() |
435 | { |
436 | Q_D(QNearFieldTagType1); |
437 | |
438 | d->m_readNdefRequestId = RequestId(new RequestIdPrivate); |
439 | |
440 | if (d->m_readNdefMessageState == QNearFieldTagType1Private::NotReadingNdefMessage) { |
441 | d->progressToNextNdefReadMessageState(); |
442 | } else { |
443 | reportError(error: QNearFieldTarget::NdefReadError, id: d->m_readNdefRequestId); |
444 | } |
445 | |
446 | return d->m_readNdefRequestId; |
447 | } |
448 | |
449 | /*! |
450 | \reimp |
451 | */ |
452 | QNearFieldTarget::RequestId QNearFieldTagType1::writeNdefMessages(const QList<QNdefMessage> &messages) |
453 | { |
454 | Q_D(QNearFieldTagType1); |
455 | |
456 | d->m_writeNdefRequestId = RequestId(new RequestIdPrivate); |
457 | |
458 | if (d->m_readNdefMessageState == QNearFieldTagType1Private::NotReadingNdefMessage && |
459 | d->m_writeNdefMessageState == QNearFieldTagType1Private::NotWritingNdefMessage) { |
460 | d->m_ndefWriteMessages = messages; |
461 | d->progressToNextNdefWriteMessageState(); |
462 | } else { |
463 | reportError(error: QNearFieldTarget::NdefWriteError, id: d->m_readNdefRequestId); |
464 | } |
465 | |
466 | return d->m_writeNdefRequestId; |
467 | } |
468 | |
469 | /*! |
470 | Returns the NFC Tag Type 1 specification version number that the tag supports. |
471 | */ |
472 | quint8 QNearFieldTagType1::version() |
473 | { |
474 | RequestId id = readByte(address: 9); |
475 | if (!waitForRequestCompleted(id)) |
476 | return 0; |
477 | |
478 | quint8 versionNumber = requestResponse(id).toUInt(); |
479 | return versionNumber; |
480 | } |
481 | |
482 | /*! |
483 | Returns the memory size in bytes of the tag. |
484 | */ |
485 | int QNearFieldTagType1::memorySize() |
486 | { |
487 | RequestId id = readByte(address: 10); |
488 | if (!waitForRequestCompleted(id)) |
489 | return 0; |
490 | |
491 | quint8 tms = requestResponse(id).toUInt(); |
492 | |
493 | return 8 * (tms + 1); |
494 | } |
495 | |
496 | /*! |
497 | Requests the identification bytes from the target. Returns a request id which can be used to |
498 | track the completion status of the request. |
499 | |
500 | Once the request completes successfully the response can be retrieved from the |
501 | requestResponse() function. The response of this request will be a QByteArray containing: HR0, |
502 | HR1, UID0, UID1, UID2 and UID3 in order. |
503 | |
504 | \sa requestCompleted(), waitForRequestCompleted() |
505 | */ |
506 | QNearFieldTarget::RequestId QNearFieldTagType1::readIdentification() |
507 | { |
508 | QByteArray command; |
509 | command.append(c: char(0x78)); // RID |
510 | command.append(c: char(0x00)); // Address (unused) |
511 | command.append(c: char(0x00)); // Data (unused) |
512 | command.append(a: uid().left(len: 4)); // 4 bytes of UID |
513 | |
514 | return sendCommand(command); |
515 | } |
516 | |
517 | /*! |
518 | Requests all data in the static memory area of the target. Returns a request id which can be |
519 | used to track the completion status of the request. |
520 | |
521 | Once the request completes successfully the response can be retrieved from the |
522 | requestResponse() function. The response of this request will be a QByteArray containing: HR0 |
523 | and HR1 followed by the 120 bytes of data stored in the static memory area of the target. |
524 | |
525 | \sa requestCompleted(), waitForRequestCompleted() |
526 | */ |
527 | QNearFieldTarget::RequestId QNearFieldTagType1::readAll() |
528 | { |
529 | QByteArray command; |
530 | command.append(c: char(0x00)); // RALL |
531 | command.append(c: char(0x00)); // Address (unused) |
532 | command.append(c: char(0x00)); // Data (unused) |
533 | command.append(a: uid().left(len: 4));// 4 bytes of UID |
534 | |
535 | return sendCommand(command); |
536 | } |
537 | |
538 | /*! |
539 | Requests a single byte from the static memory area of the tag. The \a address parameter |
540 | specifices the linear byte address to read. Returns a request id which can be used to track |
541 | the completion status of the request. |
542 | |
543 | Once the request completes successfully the response can be retrieved from the |
544 | requestResponse() function. The response of this request will be a quint8. |
545 | |
546 | \sa requestCompleted(), waitForRequestCompleted() |
547 | */ |
548 | QNearFieldTarget::RequestId QNearFieldTagType1::readByte(quint8 address) |
549 | { |
550 | if (address & 0x80) |
551 | return RequestId(); |
552 | |
553 | QByteArray command; |
554 | command.append(c: char(0x01)); // READ |
555 | command.append(c: char(address)); // Address |
556 | command.append(c: char(0x00)); // Data (unused) |
557 | command.append(a: uid().left(len: 4)); // 4 bytes of UID |
558 | |
559 | RequestId id = sendCommand(command); |
560 | |
561 | Q_D(QNearFieldTagType1); |
562 | |
563 | d->m_pendingInternalCommands.insert(akey: id, avalue: command); |
564 | |
565 | return id; |
566 | } |
567 | |
568 | /*! |
569 | Writes a single \a data byte to the linear byte \a address on the tag. If \a mode is |
570 | EraseAndWrite the byte will be erased before writing. If \a mode is WriteOnly the contents will |
571 | not be erased before writing. This is equivelant to writing the result of the bitwise OR of |
572 | \a data and the original value. |
573 | |
574 | Returns a request id which can be used to track the completion status of the request. |
575 | |
576 | Once the request completes the response can be retrieved from the requestResponse() function. |
577 | The response of this request will be a boolean value, true for success; otherwise false. |
578 | |
579 | \sa requestCompleted(), waitForRequestCompleted() |
580 | */ |
581 | QNearFieldTarget::RequestId QNearFieldTagType1::writeByte(quint8 address, quint8 data, |
582 | WriteMode mode) |
583 | { |
584 | if (address & 0x80) |
585 | return RequestId(); |
586 | |
587 | QByteArray command; |
588 | |
589 | if (mode == EraseAndWrite) |
590 | command.append(c: char(0x53)); // WRITE-E |
591 | else if (mode == WriteOnly) |
592 | command.append(c: char(0x1a)); // WRITE-NE |
593 | else |
594 | return RequestId(); |
595 | |
596 | command.append(c: char(address)); // Address |
597 | command.append(c: char(data)); // Data |
598 | command.append(a: uid().left(len: 4)); // 4 bytes of UID |
599 | |
600 | RequestId id = sendCommand(command); |
601 | |
602 | Q_D(QNearFieldTagType1); |
603 | |
604 | d->m_pendingInternalCommands.insert(akey: id, avalue: command); |
605 | |
606 | return id; |
607 | } |
608 | |
609 | /*! |
610 | Requests 128 bytes of data from the segment specified by \a segmentAddress. Returns a request |
611 | id which can be used to track the completion status of the request. |
612 | |
613 | Once the request completes successfully the response can be retrieved from the |
614 | requestResponse() function. The response of this request will be a QByteArray. |
615 | |
616 | \sa requestCompleted(), waitForRequestCompleted() |
617 | */ |
618 | QNearFieldTarget::RequestId QNearFieldTagType1::readSegment(quint8 segmentAddress) |
619 | { |
620 | if (segmentAddress & 0xf0) |
621 | return RequestId(); |
622 | |
623 | QByteArray command; |
624 | command.append(c: char(0x10)); // RSEG |
625 | command.append(c: char(segmentAddress << 4)); // Segment address |
626 | command.append(a: QByteArray(8, char(0x00))); // Data (unused) |
627 | command.append(a: uid().left(len: 4)); // 4 bytes of UID |
628 | |
629 | RequestId id = sendCommand(command); |
630 | |
631 | Q_D(QNearFieldTagType1); |
632 | |
633 | d->m_pendingInternalCommands.insert(akey: id, avalue: command); |
634 | |
635 | return id; |
636 | } |
637 | |
638 | /*! |
639 | Requests 8 bytes of data from the block specified by \a blockAddress. Returns a request id |
640 | which can be used to track the completion status of the request. |
641 | |
642 | Once the request completes successfully the response can be retrieved from the |
643 | requestResponse() function. The response of this request will be a QByteArray. |
644 | |
645 | \sa requestCompleted(), waitForRequestCompleted() |
646 | */ |
647 | QNearFieldTarget::RequestId QNearFieldTagType1::readBlock(quint8 blockAddress) |
648 | { |
649 | QByteArray command; |
650 | command.append(c: char(0x02)); // READ8 |
651 | command.append(c: char(blockAddress)); // Block address |
652 | command.append(a: QByteArray(8, char(0x00))); // Data (unused) |
653 | command.append(a: uid().left(len: 4)); // 4 bytes of UID |
654 | |
655 | RequestId id = sendCommand(command); |
656 | |
657 | Q_D(QNearFieldTagType1); |
658 | |
659 | d->m_pendingInternalCommands.insert(akey: id, avalue: command); |
660 | |
661 | return id; |
662 | } |
663 | |
664 | /*! |
665 | Writes 8 bytes of \a data to the block specified by \a blockAddress. If \a mode is |
666 | EraseAndWrite the bytes will be erased before writing. If \a mode is WriteOnly the contents |
667 | will not be erased before writing. This is equivelant to writing the result of the bitwise OR |
668 | of \a data and the original value. |
669 | |
670 | Returns a request id which can be used to track the completion status of the request. |
671 | |
672 | Once the request completes the response can be retrieved from the requestResponse() function. |
673 | The response of this request will be a boolean value, true for success; otherwise false. |
674 | |
675 | \sa requestCompleted(), waitForRequestCompleted() |
676 | */ |
677 | QNearFieldTarget::RequestId QNearFieldTagType1::writeBlock(quint8 blockAddress, |
678 | const QByteArray &data, |
679 | WriteMode mode) |
680 | { |
681 | if (data.length() != 8) |
682 | return RequestId(); |
683 | |
684 | QByteArray command; |
685 | |
686 | if (mode == EraseAndWrite) |
687 | command.append(c: char(0x54)); // WRITE-E8 |
688 | else if (mode == WriteOnly) |
689 | command.append(c: char(0x1b)); // WRITE-NE8 |
690 | else |
691 | return RequestId(); |
692 | |
693 | command.append(c: char(blockAddress)); // Block address |
694 | command.append(a: data); // Data |
695 | command.append(a: uid().left(len: 4)); // 4 bytes of UID |
696 | |
697 | RequestId id = sendCommand(command); |
698 | |
699 | Q_D(QNearFieldTagType1); |
700 | |
701 | d->m_pendingInternalCommands.insert(akey: id, avalue: command); |
702 | |
703 | return id; |
704 | } |
705 | |
706 | /*! |
707 | \reimp |
708 | */ |
709 | bool QNearFieldTagType1::handleResponse(const QNearFieldTarget::RequestId &id, |
710 | const QByteArray &response) |
711 | { |
712 | Q_D(QNearFieldTagType1); |
713 | |
714 | bool handled; |
715 | |
716 | if (d->m_pendingInternalCommands.contains(akey: id)) { |
717 | const QByteArray command = d->m_pendingInternalCommands.take(akey: id); |
718 | |
719 | QVariant decodedResponse = decodeResponse(command, response); |
720 | setResponseForRequest(id, response: decodedResponse); |
721 | |
722 | handled = true; |
723 | } else { |
724 | handled = QNearFieldTarget::handleResponse(id, response); |
725 | } |
726 | |
727 | // continue reading / writing NDEF message |
728 | if (d->m_nextExpectedRequestId == id) { |
729 | if (d->m_readNdefMessageState != QNearFieldTagType1Private::NotReadingNdefMessage) |
730 | d->progressToNextNdefReadMessageState(); |
731 | else if (d->m_writeNdefMessageState != QNearFieldTagType1Private::NotWritingNdefMessage) |
732 | d->progressToNextNdefWriteMessageState(); |
733 | } |
734 | |
735 | return handled; |
736 | } |
737 | |
738 | QT_END_NAMESPACE |
739 | |