PageRenderTime 85ms CodeModel.GetById 2ms app.highlight 75ms RepoModel.GetById 1ms app.codeStats 0ms

/examples/llcp-test-client.py

https://bitbucket.org/jialvarez/nfcpy
Python | 893 lines | 819 code | 7 blank | 67 comment | 33 complexity | 90cea7fc00c09a0db25b7f73d82db04c MD5 | raw file
  1#!/usr/bin/python
  2# -*- coding: latin-1 -*-
  3# -----------------------------------------------------------------------------
  4# Copyright 2009-2011 Stephen Tiedemann <stephen.tiedemann@googlemail.com>
  5#
  6# Licensed under the EUPL, Version 1.1 or - as soon they 
  7# will be approved by the European Commission - subsequent
  8# versions of the EUPL (the "Licence");
  9# You may not use this work except in compliance with the
 10# Licence.
 11# You may obtain a copy of the Licence at:
 12#
 13# http://www.osor.eu/eupl
 14#
 15# Unless required by applicable law or agreed to in
 16# writing, software distributed under the Licence is
 17# distributed on an "AS IS" basis,
 18# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 19# express or implied.
 20# See the Licence for the specific language governing
 21# permissions and limitations under the Licence.
 22# -----------------------------------------------------------------------------
 23#
 24# Client side implementation of an LLCP validation suite to verify
 25# inter-operability of independent implementations. This suite was
 26# primarily developed for the purpose of validating the LLCP
 27# specification before final release by the NFC Forum.
 28#
 29import logging
 30log = logging.getLogger()
 31
 32import os
 33import sys
 34import time
 35from threading import Thread
 36from collections import namedtuple
 37
 38sys.path.insert(1, os.path.split(sys.path[0])[0])
 39import nfc
 40import nfc.llcp
 41
 42default_miu = 128
 43
 44class TestError(Exception):
 45    def __init__(self, value):
 46        self.value = value
 47    def __str__(self):
 48        return str(self.value)
 49
 50def info(message, prefix="   "):
 51    log.info(prefix + message)
 52
 53def test_01():
 54    """Link activation, symmetry and deactivation
 55
 56    Verify that the LLCP Link can be activated successfully, that the
 57    symmetry procedure is performed and the link can be intentionally
 58    deactivated.
 59
 60    1. Start the MAC link activation procedure on two implementations
 61       and verify that the version number parameter is received and
 62       version number agreement is achieved.
 63
 64    2. Verify for a duration of 5 seconds that SYMM PDUs are exchanged
 65       within the Link Timout values provided by the implementations.
 66
 67    3. Perform intentional link deactivation by sending a DISC PDU to
 68       the remote Link Management component. Verify that SYMM PDUs
 69       are no longer exchanged.
 70    """
 71    info("Test 1: link activation, symmetry and deactivation", prefix="")
 72    for i in range(5):
 73        time.sleep(1)
 74        if not nfc.llcp.connected():
 75            raise TestError("connection lost before test completion")
 76
 77def test_02():
 78    """Connection-less information transfer
 79
 80    Verify that the source and destination access point address fields
 81    are correctly interpreted, the content of the information field is
 82    extracted as the service data unit and the service data unit can
 83    take any length between zero and the announced Link MIU. The LLCP
 84    Link must be activated prior to running this scenario and the Link
 85    MIU of the peer implementation must have been determined. In this
 86    scenario, sending of a service data unit (SDU) means that the SDU
 87    is carried within the information field of a UI PDU.
 88
 89    1. Send a service data unit of 128 octets length to the
 90       connection-less mode echo service and verify that the same SDU
 91       is sent back after the echo delay time.
 92
 93    2. Send within echo delay time with a time interval of at least
 94       0.5 second two consecutive service data units of 128 octets
 95       length to the connection-less mode echo service and verify that
 96       both SDUs are sent back correctly.
 97
 98    3. Send within echo delay time with a time interval of at least
 99       0.5 second three consecutive service data units of 128 octets
100       length to the connection-less mode echo service and verify that
101       the first two SDUs are sent back correctly and the third SDU is
102       discarded.
103
104    4. Send a service data unit of zero octets length to the
105       connection-less mode echo service and verify that the same zero
106       length SDU is sent back after the echo delay time.
107
108    5. Send a service data unit of maximum octets length to the
109       connection-less mode echo service and verify that the same SDU
110       is sent back after the echo delay time. Note that the maximum
111       length here must be the smaller value of both implementations
112       Link MIU.
113    """
114    TestData = namedtuple("TestData", "send recv")
115
116    def send_and_receive(socket, send_count, packet_length):
117        test_data = TestData(send=[], recv=[])
118        cl_server = nfc.llcp.getpeername(socket)
119        for i in range(1, send_count + 1):
120            data, addr = packet_length * chr(i), cl_server
121            nfc.llcp.sendto(socket, data, addr)
122            info("sent message {0}".format(i))
123            test_data.send.append((data, addr, time.time()))
124            time.sleep(0.5)
125        while nfc.llcp.poll(socket, "recv", timeout=5):
126            data, addr = nfc.llcp.recvfrom(socket)
127            test_data.recv.append((data, addr, time.time()))
128        if len(test_data.recv) == 0:
129            raise TestError("did not receive any data within 5 seconds")
130        return test_data
131
132    def run_step_1(socket):
133        info("step 1: send one default size datagram", prefix="")
134        test_data = send_and_receive(socket, 1, default_miu)
135        if not len(test_data.recv) == len(test_data.send):
136            raise TestError("received wrong number of datagrams")
137        for i in range(len(test_data.recv)):
138            send_data, send_addr, send_time = test_data.send[i]
139            recv_data, recv_addr, recv_time = test_data.recv[i]
140            if recv_addr != send_addr:
141                raise TestError("received data from different port")
142            if recv_data != send_data:
143                raise TestError("received data does not match sent data")
144            info("rcvd message {0} after {1} ms"
145                     .format(i+1, int((recv_time - send_time) * 1000)))
146        return True
147
148    def run_step_2(socket):
149        info("step 2: send two default size datagrams", prefix="")
150        test_data = send_and_receive(socket, 2, default_miu)
151        if not len(test_data.recv) == len(test_data.send):
152            raise TestError("received wrong number of datagrams")
153        for i in range(len(test_data.recv)):
154            send_data, send_addr, send_time = test_data.send[i]
155            recv_data, recv_addr, recv_time = test_data.recv[i]
156            if recv_addr != send_addr:
157                raise TestError("received data from different port")
158            if recv_data != send_data:
159                raise TestError("received data does not match sent data")
160            info("rcvd message {0} after {1} ms"
161                     .format(i+1, int((recv_time - send_time) * 1000)))
162        return True
163
164    def run_step_3(socket):
165        info("step 3: send three default size datagrams", prefix="")
166        test_data = send_and_receive(socket, 3, default_miu)
167        if not len(test_data.recv) == len(test_data.send) - 1:
168            raise TestError("received wrong number of datagrams")
169        for i in range(len(test_data.recv)):
170            send_data, send_addr, send_time = test_data.send[i]
171            recv_data, recv_addr, recv_time = test_data.recv[i]
172            if recv_addr != send_addr:
173                raise TestError("received data from different port")
174            if recv_data != send_data:
175                raise TestError("received data does not match sent data")
176            info("rcvd message {0} after {1} ms"
177                     .format(i+1, int((recv_time - send_time) * 1000)))
178        return True
179
180    def run_step_4(socket):
181        info("step 4: send one zero-length datagram", prefix="")
182        test_data = send_and_receive(socket, 1, packet_length=0)
183        if not len(test_data.recv) == len(test_data.send):
184            raise TestError("received wrong number of datagrams")
185        for i in range(len(test_data.recv)):
186            send_data, send_addr, send_time = test_data.send[i]
187            recv_data, recv_addr, recv_time = test_data.recv[i]
188            if recv_addr != send_addr:
189                raise TestError("received data from different port")
190            if recv_data != send_data:
191                raise TestError("received data does not match sent data")
192            info("rcvd message {0} after {1} ms"
193                     .format(i+1, int((recv_time - send_time) * 1000)))
194        return True
195
196    def run_step_5(socket):
197        info("step 5: send one maximum length packet", prefix="")
198        miu = nfc.llcp.getsockopt(socket, nfc.llcp.SO_SNDMIU)
199        test_data = send_and_receive(socket, 1, packet_length=miu)
200        if not len(test_data.recv) == len(test_data.send):
201            raise TestError("received wrong number of datagrams")
202        for i in range(len(test_data.recv)):
203            send_data, send_addr, send_time = test_data.send[i]
204            recv_data, recv_addr, recv_time = test_data.recv[i]
205            if recv_addr != send_addr:
206                raise TestError("received data from different port")
207            if recv_data != send_data:
208                raise TestError("received data does not match sent data")
209            info("rcvd message {0} after {1} ms"
210                     .format(i+1, int((recv_time - send_time) * 1000)))
211        return True
212
213    info("Test 2: connection-less information transfer", prefix="")
214    socket = nfc.llcp.socket(nfc.llcp.LOGICAL_DATA_LINK)
215    nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 10)
216    if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 10:
217        info("socket recv buffer set to 10")
218    else: raise TestError("could not set the socket recv buffer")
219    cl_echo_server = options.cl_echo_sap
220    if not cl_echo_server:
221        cl_echo_server = nfc.llcp.resolve("urn:nfc:sn:cl-echo")
222    if not cl_echo_server:
223        raise TestError("no connection-less echo server on peer device")
224    info("connection-less echo server on sap {0}".format(cl_echo_server))
225    nfc.llcp.connect(socket, cl_echo_server)
226    try:
227        if run_step_1(socket): info("PASS", prefix="")
228        if run_step_2(socket): info("PASS", prefix="")
229        if run_step_3(socket): info("PASS", prefix="")
230        if run_step_4(socket): info("PASS", prefix="")
231        if run_step_5(socket): info("PASS", prefix="")
232    finally:
233        nfc.llcp.close(socket)
234            
235def test_03():
236    """Connection-oriented information transfer
237
238    Verify that a data link connection can be established, a service
239    data unit is received and sent back correctly and the data link
240    connection can be terminated. The LLCP Link must be activated
241    prior to running this scenario and the connection-oriented mode
242    echo service must be in the unconnected state.  In this scenario,
243    sending of a service data unit (SDU) means that the SDU is carried
244    within the information field of an I PDU.
245
246    1. Send a CONNECT PDU to the connection-oriented mode echo service
247       and verify that the connection request is acknowledged with a
248       CC PDU. The CONNECT PDU shall encode the RW parameter with a
249       value of 2. Verify that the CC PDU encodes the RW parameter
250       with a value of 2 (as specified for the echo server).
251
252    2. Send a single service data unit of 128 octets length over the
253       data link connection and verify that the echo service sends an
254       RR PDU before returning the same SDU after the echo delay time.
255
256    3. Send a DISC PDU to terminate the data link connection and
257       verify that the echo service responds with a correct DM PDU.
258    """
259    info("Test 3: connection-oriented information transfer", prefix="")
260    socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
261    nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 2)
262    if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 2:
263        info("socket recv window set 2")
264    else: raise TestError("could not set the socket recv window")
265    co_echo_server = options.co_echo_sap
266    if not co_echo_server:
267        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
268    if not co_echo_server:
269        raise TestError("no connection-mode echo server on peer device")
270    info("connection-mode echo server on sap {0}".format(co_echo_server))
271    nfc.llcp.connect(socket, co_echo_server)
272    peer_sap = nfc.llcp.getpeername(socket)
273    info("connected with sap {0}".format(peer_sap))
274    nfc.llcp.send(socket, default_miu * "\xFF")
275    t0 = time.time()
276    info("sent one information pdu")
277    if nfc.llcp.poll(socket, "acks", timeout = 5):
278        elapsed = time.time() - t0
279        info("got confirm after {0:.3f}".format(elapsed))
280        if not elapsed < 1.9:
281            raise TestError("no confirmation within 1.9 seconds")
282        nfc.llcp.recv(socket)
283        elapsed = time.time() - t0
284        info("got message after {0:.3f}".format(time.time() - t0))
285        if not elapsed > 2.0:
286            raise TestError("echo'd data received too early")
287    else: raise TestError("no data received within 5 seconds")
288    nfc.llcp.close(socket)
289
290def test_04():
291    """Send and receive sequence number handling
292
293    Verify that a sequence of service data units that causes the send
294    and receive sequence numbers to take all possible values is
295    received and sent back correctly. The LLCP Link must be activated
296    prior to running this scenario and the connection-oriented mode
297    echo service must be in the unconnected state. In this scenario,
298    sending of a service data unit (SDU) means that the SDU is carried
299    within the information field of an I PDU.
300
301    1. Send a CONNECT PDU to the connection-oriented mode echo service
302       and verify that the connection request is acknowledged with a
303       CC PDU. The CONNECT PDU shall encode the RW parameter with a
304       value of 2. Verify that the CC PDU encodes the RW parameter
305       with a value of 2 (as specified for the echo server).
306
307    2. Send a sequence of at least 16 data units of each 128 octets
308       length over the data link connection and verify that all SDUs
309       are sent back correctly.
310
311    3. Send a DISC PDU to terminate the data link connection and
312       verify that the echo service responds with a correct DM PDU.
313    """
314    sent_data = []
315    rcvd_data = []
316
317    def receiver(socket, rcvd_data):
318        while nfc.llcp.poll(socket, "recv", timeout=5):
319            data = nfc.llcp.recv(socket)
320            if data: rcvd_data.append((data, time.time()))
321
322    info("Test 4: send and receive sequence number handling", prefix="")
323    socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
324    nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 2)
325    if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 2:
326        info("receive window set to 2")
327    else: raise TestError("failed to set receive window to 2")
328    co_echo_server = options.co_echo_sap
329    if not co_echo_server:
330        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
331    if not co_echo_server:
332        raise TestError("no connection-mode echo server on peer device")
333    info("connection-mode echo server on sap {0}".format(co_echo_server))
334    recv_thread = Thread(target=receiver, args=[socket, rcvd_data])
335    try:
336        nfc.llcp.connect(socket, co_echo_server)
337        peer_sap = nfc.llcp.getpeername(socket)
338        info("connected with sap {0}".format(peer_sap))
339        recv_thread.start()
340        count = 20
341        info("now sending {0} messages".format(count))
342        for i in range(count):
343            data = default_miu * chr(i)
344            if nfc.llcp.send(socket, data):
345                info("sent message {0}".format(i+1))
346                sent_data.append((data, time.time()))
347        recv_thread.join()
348        for i in range(count):
349            r = "correct" if rcvd_data[i][0] == rcvd_data[i][0] else "wrong"
350            t = rcvd_data[i][1] - sent_data[i][1]
351            info("message {i:2} received after {t:.3f} sec was {r}"
352                     .format(i=i, t=t, r=r))
353    finally:
354        try: recv_thread.join()
355        except RuntimeError: pass # wasn't started
356        nfc.llcp.close(socket)
357
358def test_05():
359    """Handling of receiver busy condition
360
361    Verify the handling of a busy condition. The LLCP Link must be
362    activated prior to running this scenario and the
363    connection-oriented mode echo service must be in the unconnected
364    state.  In this scenario, sending of a service data unit (SDU)
365    shall mean that the SDU is carried within the information field of
366    an I PDU.
367
368    1. Send a CONNECT PDU to the connection-oriented mode echo service
369       and verify that the connect request is acknowledged with a CC
370       PDU. The CONNECT PDU shall encode the RW parameter with a value
371       of 0. Verify that the CC PDU encodes the RW parameter with a
372       value of 2 (as specified for the echo server).
373
374    2. Send four service data units of 128 octets length over the data
375       link connection and verify that the echo service enters the
376       busy state when acknowledging the last packet.
377
378    3. Send a DISC PDU to terminate the data link connection and
379       verify that the echo service responds with a correct DM PDU.
380    """
381    info("Test 5: handling of receiver busy condition", prefix="")
382    socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
383    nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 0)
384    if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 0:
385        info("receive window set to 0")
386    else: raise TestError("failed to set receive window to 0")
387    co_echo_server = options.co_echo_sap
388    if not co_echo_server:
389        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
390    if not co_echo_server:
391        raise TestError("no connection-mode echo server on peer device")
392    info("connection-mode echo server on sap {0}".format(co_echo_server))
393    try:
394        nfc.llcp.connect(socket, co_echo_server)
395        peer_sap = nfc.llcp.getpeername(socket)
396        info("connected with sap {0}".format(peer_sap))
397        info("now sending 4 messages")
398        for i in range(4):
399            data = default_miu * chr(i)
400            if nfc.llcp.send(socket, data):
401                info("sent message {0}".format(i+1))
402        for i in range(4):
403            time.sleep(1.0)
404            if nfc.llcp.getsockopt(socket, nfc.llcp.SO_SNDBSY):
405                info("connection-mode echo server entered busy state")
406                break
407        else:
408            raise TestError("did not recognize server busy state")
409    finally:
410        nfc.llcp.close(socket)
411
412def test_06():
413    """Rejection of connect request
414
415    Verify that an attempt to establish a second connection with the
416    connection-oriented mode echo service is rejected. The LLCP Link
417    must be activated prior to running this scenario.
418
419    1. Send a first CONNECT PDU to the connection-oriented mode echo
420       service and verify that the connect request is acknowledged
421       with a CC PDU.
422
423    2. Send a second CONNECT PDU to the connection-oriented mode echo
424       service and verify that the connect request is rejected with a
425       DM PDU and appropriate reason code.
426
427    3. Send a service data unit of 128 octets length over the data
428       link connection and verify that the echo service returns the
429       same SDU after the echo delay time.
430
431    4. Send a DISC PDU to terminate the data link connection and
432       verify that the echo service responds with a correct DM PDU.
433
434    """
435    info("Test 6: rejection of connect request", prefix="")
436    socket1 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
437    socket2 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
438    co_echo_server = options.co_echo_sap
439    if not co_echo_server:
440        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
441    if not co_echo_server:
442        raise TestError("no connection-mode echo server on peer device")
443    info("connection-mode echo server on sap {0}".format(co_echo_server))
444    try:
445        nfc.llcp.connect(socket1, co_echo_server)
446        peer_sap = nfc.llcp.getpeername(socket1)
447        info("first connection established with sap {0}".format(peer_sap))
448        try: nfc.llcp.connect(socket2, co_echo_server)
449        except nfc.llcp.ConnectRefused as e:
450            info("second connection rejected with reason {0}".format(e.reason))
451        else: raise TestError("second connection not rejected")
452        finally: nfc.llcp.close(socket2)
453    finally:
454        nfc.llcp.close(socket1)
455
456def test_07():
457    """Connect by service name
458
459    Verify that a data link connection can be established by
460    specifying a service name. The LLCP Link must be activated prior
461    to running this scenario and the connection-oriented mode echo
462    service must be in the unconnected state.
463
464    1. Send a CONNECT PDU with an SN parameter that encodes the value
465       "urn:nfc:sn:co-echo" to the service discovery service access
466       point address and verify that the connect request is
467       acknowledged with a CC PDU.
468
469    2. Send a service data unit over the data link connection and
470       verify that it is sent back correctly.
471
472    3. Send a DISC PDU to terminate the data link connection and
473       verify that the echo service responds with a correct DM PDU.
474    """
475    info("Test 7: connect by service name", prefix="")
476    socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
477    try:
478        nfc.llcp.connect(socket, "urn:nfc:sn:co-echo")
479        info("connected to service 'urn:nfc:sn:co-echo'")
480        peer_sap = nfc.llcp.getpeername(socket)
481        if peer_sap == 1:
482            raise TestError("connection established with SDP port")
483        info("connection established with sap {0}".format(peer_sap))
484        if nfc.llcp.send(socket, "here's nfcpy"):
485            t0 = time.time()
486            info("sent test message")
487            if nfc.llcp.poll(socket, "recv", timeout=5):
488                if nfc.llcp.recv(socket) == "here's nfcpy":
489                    info("got echo after {0:.3f} sec".format(time.time()-t0))
490                else: raise TestError("received wrong data from echo server")
491            else: raise TestError("no echo response within 5 seconds")
492        else: raise TestError("failed to send data")
493    finally:
494        nfc.llcp.close(socket)
495    try:
496        socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
497        nfc.llcp.connect(socket, "urn:nfc:sn:co-echo-test")
498        raise TestError("connect 'co-echo-test' rejected")
499    except nfc.llcp.ConnectRefused as e:
500        info("connect 'co-echo-test' rejected with reason {0}".format(e.reason))
501    finally:
502        nfc.llcp.close(socket)
503
504def test_08():
505    """Aggregation and disaggregation
506
507    Verify that the aggregation procedure is performed correctly. The
508    LLCP Link must be activated prior to running this scenario.  In
509    this scenario, sending of a service data unit (SDU) shall mean
510    that the SDU is carried within the information field of a UI PDU.
511
512    1. Send two service data units of 50 octets length to the
513       connection-less mode echo service such that the two resulting
514       UI PDUs will be aggregated into a single AGF PDU by the LLC
515       sublayer. Verify that both SDUs are sent back correctly and in
516       the same order.
517
518    2. Send three service data units of 50 octets length to the
519       connection-less mode echo service such that the three resulting
520       UI PDUs will be aggregated into a single AGF PDU by the LLC
521       sublayer. Verify that the two first SDUs are sent back
522       correctly and the third SDU is discarded.
523    """
524    import nfc.llcp.pdu
525    info("Test 8: aggregation and disaggregation", prefix="")
526    try:
527        socket = nfc.llcp.socket(nfc.llcp.llc.RAW_ACCESS_POINT)
528        nfc.llcp.bind(socket, None)
529        nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 10)
530        if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) != 10:
531            raise TestError("could not set the socket recv buffer")
532        info("socket recv buffer set to 10")
533        cl_echo_server = options.cl_echo_sap
534        if not cl_echo_server:
535            cl_echo_server = nfc.llcp.resolve("urn:nfc:sn:cl-echo")
536        if not cl_echo_server:
537            raise TestError("no connection-less echo server on peer device")
538        info("connection-less echo server on sap {0}".format(cl_echo_server))
539        addr = nfc.llcp.getsockname(socket)
540        UI = nfc.llcp.pdu.UnnumberedInformation
541        pdu1 = UI(cl_echo_server, addr, 50*"\x01")
542        pdu2 = UI(cl_echo_server, addr, 50*"\x02")
543        pdu3 = UI(cl_echo_server, addr, 50*"\x03")
544
545        info("step 1: send two datagrams with 50 byte payload")
546        agf = nfc.llcp.pdu.AggregatedFrame(aggregate=[pdu1, pdu2])
547        nfc.llcp.send(socket, agf)
548        if not nfc.llcp.poll(socket, "recv", timeout=5):
549            raise TestError("did not receive first message within 5 sec")
550        if not nfc.llcp.recv(socket).sdu == 50*"\x01":
551            raise TestError("first message came back wrong")
552        info("received first message")
553        if not nfc.llcp.poll(socket, "recv", timeout=5):
554            raise TestError("did not receive second message within 5 sec")
555        if not nfc.llcp.recv(socket).sdu == 50*"\x02":
556            raise TestError("second message came back wrong")
557        info("received second message")
558
559        info("step2: send three datagrams with 50 byte payload")
560        agf = nfc.llcp.pdu.AggregatedFrame(aggregate=[pdu1, pdu2, pdu3])
561        nfc.llcp.send(socket, agf)
562        if not nfc.llcp.poll(socket, "recv", timeout=5):
563            raise TestError("did not receive first message within 5 sec")
564        if not nfc.llcp.recv(socket).sdu == 50*"\x01":
565            raise TestError("first message came back wrong")
566        info("received first message")
567        if not nfc.llcp.poll(socket, "recv", timeout=5):
568            raise TestError("did not receive second message within 5 sec")
569        if not nfc.llcp.recv(socket).sdu == 50*"\x02":
570            raise TestError("second message came back wrong")
571        info("received second message")
572        if nfc.llcp.poll(socket, "recv", timeout=5):
573            raise TestError("received third message")
574        info("did not receive third message within 5 sec")
575    finally:
576        nfc.llcp.close(socket)
577
578def test_09():
579    """Service name lookup
580
581    Verify that a service name is correctly resolved into a service
582    access point address by the remote LLC. The LLCP Link must be
583    activated prior to running this scenario.  In this scenario,
584    sending of a service data unit (SDU) shall mean that the SDU is
585    carried within the information field of a UI PDU.
586
587    1. Send an SNL PDU with an SDREQ parameter in the information
588       field that encodes the value "urn:nfc:sn:sdp" to the service
589       discovery service access point address and verify that the
590       request is responded with an SNL PDU that contains an SDRES
591       parameter with the SAP value '1' and a TID value that is the
592       same as the value encoded in the antecedently transmitted SDREQ
593       parameter.
594
595    2. Send an SNL PDU with an SDREQ parameter in the information
596       field that encodes the value "urn:nfc:sn:cl-echo" to the
597       service discovery service access point address and verify that
598       the request is responded with an SNL PDU that contains an SDRES
599       parameter with a SAP value other than '0' and a TID value that
600       is the same as the value encoded in the antecedently
601       transmitted SDREQ parameter.
602
603    3. Send a service data unit of 128 octets length to the service
604       access point address received in step 2 and verify that the
605       same SDU is sent back after the echo delay time.
606
607    4. Send an SNL PDU with an SDREQ parameter in the information
608       field that encodes the value "urn:nfc:sn:sdp-test" to the
609       service discovery service access point address and verify that
610       the request is responded with an SNL PDU that contains an SDRES
611       parameter with the SAP value '0' and a TID value that is the
612       same as the value encoded in the antecedently transmitted SDREQ
613       parameter.
614    """
615    info("Test 9: service name lookup", prefix="")
616    addr = nfc.llcp.resolve("urn:nfc:sn:sdp")
617    if not addr:
618        raise TestError("no answer for 'urn:nfc:sn:sdp' lookup")
619    info("step 1: resolved 'urn:nfc:sn:sdp' to sap {0}".format(addr))
620    addr = nfc.llcp.resolve("urn:nfc:sn:cl-echo")
621    if not addr:
622        raise TestError("no answer for 'urn:nfc:sn:cl-echo' lookup")
623    info("step 2: resolved 'urn:nfc:sn:cl-echo' to sap {0}".format(addr))
624    socket = nfc.llcp.socket(nfc.llcp.LOGICAL_DATA_LINK)
625    t0 = time.time()
626    if nfc.llcp.sendto(socket, 128 * "\xA9", addr):
627        info("step 3: sent 128 byte message to sap {0}".format(addr))
628        if not nfc.llcp.poll(socket, "recv", timeout=5):
629            raise TestError("did not receive echo within 5 seconds")
630        data, peer = nfc.llcp.recvfrom(socket)
631        if not data == 128 * "\xA9":
632            raise TestError("received wrong data in step 3")
633        if not peer == addr:
634            raise TestError("received from wrong sap in step 3")
635        t1 = time.time()
636        info("step 3: received echo after {0:.3} seconds".format(t1-t0))
637    addr = nfc.llcp.resolve("urn:nfc:sn:sdp-test")
638    if not addr == 0:
639        raise TestError("'urn:nfc:sn:sdp-test' did not yield 0")
640    info("step 4: resolved 'urn:nfc:sn:sdp-test' as {0}".format(addr))
641
642def test_10():
643    import nfc.llcp.pdu
644    info("Test 10: exceed the maximum information unit size", prefix="")
645    co_echo_server = options.co_echo_sap
646    if not co_echo_server:
647        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
648    if not co_echo_server:
649        raise TestError("no connection-mode echo server on peer device")
650    info("connection-mode echo server on sap {0}".format(co_echo_server))
651    dlc_socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
652    raw_socket = nfc.llcp.socket(nfc.llcp.llc.RAW_ACCESS_POINT)
653    try:
654        nfc.llcp.connect(dlc_socket, co_echo_server)
655        addr = nfc.llcp.getsockname(dlc_socket)
656        peer = nfc.llcp.getpeername(dlc_socket)
657        info("connected with sap {0}".format(peer))
658        send_miu = nfc.llcp.getsockopt(dlc_socket, nfc.llcp.SO_SNDMIU)
659        info("the peers receive MIU is {0} octets".format(send_miu))
660        sdu = (send_miu + 1) * '\x00'
661        pdu = nfc.llcp.pdu.Information(dsap=peer, ssap=addr, sdu=sdu)
662        pdu.ns, pdu.nr = 0, 0
663        nfc.llcp.send(raw_socket, pdu)
664        nfc.llcp.recv(dlc_socket)
665    except nfc.llcp.Error as e:
666        info(str(e))
667    finally:
668        nfc.llcp.close(dlc_socket)
669        nfc.llcp.close(raw_socket)
670
671def test_11():
672    import nfc.llcp.pdu
673    info("Test 11: generate invalid send sequence number", prefix="")
674    co_echo_server = options.co_echo_sap
675    if not co_echo_server:
676        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
677    if not co_echo_server:
678        raise TestError("no connection-mode echo server on peer device")
679    info("connection-mode echo server on sap {0}".format(co_echo_server))
680    dlc_socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
681    raw_socket = nfc.llcp.socket(nfc.llcp.llc.RAW_ACCESS_POINT)
682    try:
683        nfc.llcp.connect(dlc_socket, co_echo_server)
684        addr = nfc.llcp.getsockname(dlc_socket)
685        peer = nfc.llcp.getpeername(dlc_socket)
686        info("connected with sap {0}".format(peer))
687        pdu = nfc.llcp.pdu.Information(dsap=peer, ssap=addr, sdu="wrong N(S)")
688        pdu.ns, pdu.nr = 15, 0
689        nfc.llcp.send(raw_socket, pdu)
690        nfc.llcp.recv(dlc_socket)
691    except nfc.llcp.Error as e:
692        info(str(e))
693    finally:
694        nfc.llcp.close(dlc_socket)
695        nfc.llcp.close(raw_socket)
696
697def test_12():
698    info("Test 12: maximum data size on data link connection", prefix="")
699    socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
700    nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 2)
701    if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 2:
702        info("socket recv window set 2")
703    else: raise TestError("could not set the socket recv window")
704    nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVMIU, 300)
705    co_echo_server = options.co_echo_sap
706    if not co_echo_server:
707        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
708    if not co_echo_server:
709        raise TestError("no connection-mode echo server on peer device")
710    info("connection-mode echo server on sap {0}".format(co_echo_server))
711    nfc.llcp.connect(socket, co_echo_server)
712    peer_sap = nfc.llcp.getpeername(socket)
713    info("connected with sap {0}".format(peer_sap))
714    miu = nfc.llcp.getsockopt(socket, nfc.llcp.SO_SNDMIU)
715    nfc.llcp.send(socket, miu * "\xFF")
716    t0 = time.time()
717    info("sent one information pdu")
718    if nfc.llcp.poll(socket, "acks", timeout = 5):
719        elapsed = time.time() - t0
720        info("got confirm after {0:.3f}".format(elapsed))
721        if not elapsed < 1.9:
722            raise TestError("no confirmation within 1.9 seconds")
723        if not nfc.llcp.poll(socket, "recv", timeout=5):
724            raise TestError("did not receive second message within 5 sec")
725        data = nfc.llcp.recv(socket)
726        info("got message after {0:.3f}".format(time.time() - t0))
727    else: raise TestError("no data received within 5 seconds")
728    nfc.llcp.close(socket)
729
730def test_13():
731    info("Test 13: DLC connect - release - connect", prefix="")
732    socket1 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
733    socket2 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION)
734    co_echo_server = options.co_echo_sap
735    if not co_echo_server:
736        co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo")
737    if not co_echo_server:
738        raise TestError("no connection-mode echo server on peer device")
739    info("connection-mode echo server on sap {0}".format(co_echo_server))
740    try:
741        nfc.llcp.connect(socket1, co_echo_server)
742        peer_sap = nfc.llcp.getpeername(socket1)
743        info("first connection established with sap {0}".format(peer_sap))
744        nfc.llcp.send(socket1, "I'm the first connection")
745        assert(nfc.llcp.recv(socket1) == "I'm the first connection")
746        nfc.llcp.close(socket1)
747        info("first connection terminated")
748        nfc.llcp.connect(socket2, co_echo_server)
749        peer_sap = nfc.llcp.getpeername(socket2)
750        info("second connection established with sap {0}".format(peer_sap))
751        nfc.llcp.send(socket2, "I'm the second connection")
752        assert(nfc.llcp.recv(socket2) == "I'm the second connection")
753        nfc.llcp.close(socket2)
754    finally:
755        pass
756
757
758def main():
759    llcp_config = {'recv-miu': options.link_miu, 'send-lto': 500}
760    if options.quirks == "android":
761        llcp_config['send-agf'] = False
762
763    general_bytes = nfc.llcp.startup(llcp_config)
764    for device in options.device:
765        try: clf = nfc.ContactlessFrontend(device); break
766        except LookupError: pass
767    else: return
768
769    peer = None
770    try:
771        while True:
772            if options.mode == "target" or options.mode is None:
773                listen_time = 250 + ord(os.urandom(1))
774                peer = clf.listen(listen_time, general_bytes)
775                if isinstance(peer, nfc.DEP):
776                    if peer.general_bytes.startswith("Ffm"):
777                        break
778            if options.mode == "initiator" or options.mode is None:
779                peer = clf.poll(general_bytes)
780                if isinstance(peer, nfc.DEP):
781                    if peer.general_bytes.startswith("Ffm"):
782                        if options.quirks == "android":
783                            # Google Nexus S does not receive the first
784                            # packet if we send immediately.
785                            time.sleep(0.1)
786                        break
787    except KeyboardInterrupt:
788        log.info("aborted by user")
789        clf.close()
790        return
791
792    nfc.llcp.activate(peer)
793    time.sleep(0.5)
794    
795    if not options.run_test:
796        log.info("no test specified")
797
798    test_suite = [test_01, test_02, test_03, test_04, test_05,
799                  test_06, test_07, test_08, test_09, test_10,
800                  test_11, test_12, test_13]
801    try:
802        for test in options.run_test:
803            if test > 0 and test <= len(test_suite):
804                try:
805                    test_suite[test-1]()
806                    log.info("PASS")
807                except TestError as error:
808                    log.error("FAIL: {0}".format(error))
809            else: log.info("invalid test number '{0}'".format(test))
810    except KeyboardInterrupt:
811        log.info("aborted by user")
812        for thread in threading.enumerate():
813            log.info(thread.name)
814    finally:
815        nfc.llcp.shutdown()
816        clf.close()
817        log.info("I was the " + peer.role)
818
819
820if __name__ == '__main__':
821    from optparse import OptionParser, OptionGroup
822    parser = OptionParser()
823    parser.add_option("-t", "--test", type="int", default=[],
824                      action="append", dest="run_test", metavar="N",
825                      help="run test number <N>")
826    parser.add_option("-q", default=True,
827                      action="store_false", dest="verbose",
828                      help="be quiet, only print errors")
829    parser.add_option("-d", type="string", default=[],
830                      action="append", dest="debug", metavar="MODULE",
831                      help="print debug messages for MODULE")
832    parser.add_option("-f", type="string",
833                      action="store", dest="logfile",
834                      help="write log messages to LOGFILE")
835    parser.add_option("--device", type="string", default=[],
836                      action="append", dest="device", metavar="SPEC",
837                      help="use only device(s) according to SPEC: "\
838                          "usb[:vendor[:product]] (vendor and product in hex) "\
839                          "usb[:bus[:dev]] (bus and device number in decimal) "\
840                          "tty[:(usb|com)[:port]] (usb virtual or com port)")
841    parser.add_option("--mode", type="choice", default=None,
842                      choices=["target", "initiator"],
843                      action="store", dest="mode",
844                      help="restrict mode to 'target' or 'initiator'")
845    parser.add_option("--cl-echo", type="int", default=None,
846                      action="store", dest="cl_echo_sap", metavar="SAP",
847                      help="connection-less echo server address")
848    parser.add_option("--co-echo", type="int", default=None,
849                      action="store", dest="co_echo_sap", metavar="SAP",
850                      help="connection-oriented echo server address")
851    parser.add_option("--link-miu", type="int", default=2175,
852                      action="store", dest="link_miu", metavar="MIU",
853                      help="set maximum information unit size to MIU")
854    parser.add_option("--quirks", type="string",
855                      action="store", dest="quirks", metavar="choice",
856                      help="quirks mode, choices are 'android'")
857
858    global options
859    options, args = parser.parse_args()
860
861    verbosity = logging.INFO if options.verbose else logging.ERROR
862    logging.basicConfig(level=verbosity, format='%(message)s')
863
864    if options.logfile:
865        logfile_format = '%(asctime)s %(levelname)-5s [%(name)s] %(message)s'
866        logfile = logging.FileHandler(options.logfile, "w")
867        logfile.setFormatter(logging.Formatter(logfile_format))
868        logfile.setLevel(logging.DEBUG)
869        logging.getLogger('').addHandler(logfile)
870
871    import inspect, os, os.path
872    nfcpy_path = os.path.dirname(inspect.getfile(nfc))
873    for name in os.listdir(nfcpy_path):
874        if os.path.isdir(os.path.join(nfcpy_path, name)):
875            logging.getLogger("nfc."+name).setLevel(verbosity)
876        elif name.endswith(".py") and name != "__init__.py":
877            logging.getLogger("nfc."+name[:-3]).setLevel(verbosity)
878            
879    if options.debug:
880        logging.getLogger('').setLevel(logging.DEBUG)
881        logging.getLogger('nfc').setLevel(logging.DEBUG)
882        for module in options.debug:
883            log.info("enable debug output for module '{0}'".format(module))
884            logging.getLogger(module).setLevel(logging.DEBUG)
885
886    try: options.run_test = [int(t) for t in options.run_test]
887    except ValueError: log.error("non-integer test number"); sys.exit(-1)
888
889    if len(options.device) == 0:
890        # search and use first
891        options.device = ["",]
892        
893    main()