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