pktradio_loopback.c 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. /****************************************************************************
  2. * wireless/pktradio/pktradio_loopback.c
  3. *
  4. * Copyright (C) 2017 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <stdint.h>
  40. #include <stdbool.h>
  41. #include <string.h>
  42. #include <errno.h>
  43. #include <assert.h>
  44. #include <debug.h>
  45. #include <arpa/inet.h>
  46. #include <net/if.h>
  47. #include <nuttx/wdog.h>
  48. #include <nuttx/wqueue.h>
  49. #include <nuttx/mm/iob.h>
  50. #include <nuttx/net/net.h>
  51. #include <nuttx/net/ip.h>
  52. #include <nuttx/net/radiodev.h>
  53. #include <nuttx/net/sixlowpan.h>
  54. #include <nuttx/wireless/pktradio.h>
  55. #ifdef CONFIG_PKTRADIO_LOOPBACK
  56. /****************************************************************************
  57. * Pre-processor Definitions
  58. ****************************************************************************/
  59. /* We need to have the work queue to handle SPI interrupts */
  60. #if !defined(CONFIG_SCHED_WORKQUEUE)
  61. # error Worker thread support is required (CONFIG_SCHED_WORKQUEUE)
  62. #else
  63. # if defined(CONFIG_SCHED_LPWORK)
  64. # define LPBKWORK LPWORK
  65. # elif defined(CONFIG_SCHED_HPWORK)
  66. # define LPBKWORK HPWORK
  67. # else
  68. # error Neither CONFIG_SCHED_LPWORK nor CONFIG_SCHED_HPWORK defined
  69. # endif
  70. #endif
  71. #ifndef CONFIG_WIRELESS_PKTRADIO
  72. # error CONFIG_WIRELESS_PKTRADIO=y is required.
  73. #endif
  74. #ifndef CONFIG_NET_6LOWPAN
  75. # error CONFIG_NET_6LOWPAN=y is required.
  76. #endif
  77. #if (CONFIG_PKTRADIO_ADDRLEN != 1) && (CONFIG_PKTRADIO_ADDRLEN != 2) && \
  78. (CONFIG_PKTRADIO_ADDRLEN != 8)
  79. # error No support for CONFIG_PKTRADIO_ADDRLEN other than {1,2,8}
  80. #endif
  81. /* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
  82. #define LO_WDDELAY (1*CLK_TCK)
  83. /* Fake value for MAC header length */
  84. #if CONFIG_IOB_BUFSIZE > 40
  85. # define MAC_HDRLEN 4
  86. #else
  87. # define MAC_HDRLEN 0
  88. #endif
  89. /****************************************************************************
  90. * Private Types
  91. ****************************************************************************/
  92. /* The lo_driver_s encapsulates all state information for a single hardware
  93. * interface
  94. */
  95. struct lo_driver_s
  96. {
  97. bool lo_bifup; /* true:ifup false:ifdown */
  98. bool lo_pending; /* True: TX poll pending */
  99. uint8_t lo_panid[2]; /* Fake PAN ID for testing */
  100. WDOG_ID lo_polldog; /* TX poll timer */
  101. struct work_s lo_work; /* For deferring poll work to the work queue */
  102. FAR struct iob_s *lo_head; /* Head of IOBs queued for loopback */
  103. FAR struct iob_s *lo_tail; /* Tail of IOBs queued for loopback */
  104. /* This holds the information visible to the NuttX network */
  105. struct radio_driver_s lo_radio; /* Interface understood by the network */
  106. };
  107. /****************************************************************************
  108. * Private Data
  109. ****************************************************************************/
  110. static struct lo_driver_s g_loopback;
  111. #ifdef CONFIG_NET_6LOWPAN
  112. static struct sixlowpan_reassbuf_s g_iobuffer;
  113. #endif
  114. static uint8_t g_mac_addr[CONFIG_PKTRADIO_ADDRLEN] =
  115. {
  116. #if CONFIG_PKTRADIO_ADDRLEN == 1
  117. 0xab
  118. #elif CONFIG_PKTRADIO_ADDRLEN == 2
  119. 0xab, 0xcd
  120. #elif CONFIG_PKTRADIO_ADDRLEN == 8
  121. 0x0c, 0xfa, 0xde, 0x00, 0xde, 0xad, 0xbe, 0xef
  122. #endif
  123. };
  124. /****************************************************************************
  125. * Private Function Prototypes
  126. ****************************************************************************/
  127. /* Utility functions */
  128. static void lo_addr2ip(FAR struct net_driver_s *dev);
  129. static inline void lo_netmask(FAR struct net_driver_s *dev);
  130. /* Polling logic */
  131. static int lo_loopback(FAR struct net_driver_s *dev);
  132. static void lo_loopback_work(FAR void *arg);
  133. static void lo_poll_work(FAR void *arg);
  134. static void lo_poll_expiry(int argc, wdparm_t arg, ...);
  135. /* NuttX callback functions */
  136. static int lo_ifup(FAR struct net_driver_s *dev);
  137. static int lo_ifdown(FAR struct net_driver_s *dev);
  138. static void lo_txavail_work(FAR void *arg);
  139. static int lo_txavail(FAR struct net_driver_s *dev);
  140. #ifdef CONFIG_NET_MCASTGROUP
  141. static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac);
  142. static int lo_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac);
  143. #endif
  144. #ifdef CONFIG_NETDEV_IOCTL
  145. static int lo_ioctl(FAR struct net_driver_s *dev, int cmd,
  146. unsigned long arg);
  147. #endif
  148. static int lo_get_mhrlen(FAR struct radio_driver_s *netdev,
  149. FAR const void *meta);
  150. static int lo_req_data(FAR struct radio_driver_s *netdev,
  151. FAR const void *meta, FAR struct iob_s *framelist);
  152. static int lo_properties(FAR struct radio_driver_s *netdev,
  153. FAR struct radiodev_properties_s *properties);
  154. /****************************************************************************
  155. * Private Functions
  156. ****************************************************************************/
  157. /****************************************************************************
  158. * Name: lo_addr2ip
  159. *
  160. * Description:
  161. * Create a MAC-based IP address from the IEEE 802.15.14 short or extended
  162. * address assigned to the node.
  163. *
  164. * 128 112 96 80 64 48 32 16
  165. * ---- ---- ---- ---- ---- ---- ---- ----
  166. * fe80 0000 0000 0000 0000 00ff fe00 xxxx 2-byte short address IEEE 48-bit MAC
  167. * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte extended address IEEE EUI-64
  168. *
  169. ****************************************************************************/
  170. static void lo_addr2ip(FAR struct net_driver_s *dev)
  171. {
  172. /* Set the MAC address as the saddr */
  173. dev->d_mac.radio.nv_addrlen = CONFIG_PKTRADIO_ADDRLEN;
  174. memcpy(dev->d_mac.radio.nv_addr, g_mac_addr, CONFIG_PKTRADIO_ADDRLEN);
  175. /* Set the IP address */
  176. dev->d_ipv6addr[0] = HTONS(0xfe80);
  177. dev->d_ipv6addr[1] = 0;
  178. dev->d_ipv6addr[2] = 0;
  179. dev->d_ipv6addr[3] = 0;
  180. #if CONFIG_PKTRADIO_ADDRLEN == 1
  181. /* Set the IP address based on the 1 byte address */
  182. dev->d_ipv6addr[4] = 0;
  183. dev->d_ipv6addr[5] = HTONS(0x00ff);
  184. dev->d_ipv6addr[6] = HTONS(0xfe00);
  185. dev->d_ipv6addr[7] = (uint16_t)g_mac_addr[0] << 8;
  186. #elif CONFIG_PKTRADIO_ADDRLEN == 2
  187. /* Set the IP address based on the 2 byte address */
  188. dev->d_ipv6addr[4] = 0;
  189. dev->d_ipv6addr[5] = HTONS(0x00ff);
  190. dev->d_ipv6addr[6] = HTONS(0xfe00);
  191. dev->d_ipv6addr[7] = (uint16_t)g_mac_addr[0] << 8 | (uint16_t)g_mac_addr[1];
  192. #elif CONFIG_PKTRADIO_ADDRLEN == 8
  193. /* Set the IP address based on the 8-byte address */
  194. dev->d_ipv6addr[4] = (uint16_t)g_mac_addr[0] << 8 | (uint16_t)g_mac_addr[1];
  195. dev->d_ipv6addr[5] = (uint16_t)g_mac_addr[2] << 8 | (uint16_t)g_mac_addr[3];
  196. dev->d_ipv6addr[6] = (uint16_t)g_mac_addr[4] << 8 | (uint16_t)g_mac_addr[5];
  197. dev->d_ipv6addr[7] = (uint16_t)g_mac_addr[6] << 8 | (uint16_t)g_mac_addr[7];
  198. #endif
  199. }
  200. /****************************************************************************
  201. * Name: lo_netmask
  202. *
  203. * Description:
  204. * Create a netmask of a MAC-based IP address which may be based on either
  205. * the IEEE 802.15.14 short or extended address of the MAC.
  206. *
  207. * 128 112 96 80 64 48 32 16
  208. * ---- ---- ---- ---- ---- ---- ---- ----
  209. * fe80 0000 0000 0000 0000 00ff fe00 xxxx 2-byte short address IEEE 48-bit MAC
  210. * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte extended address IEEE EUI-64
  211. *
  212. ****************************************************************************/
  213. static inline void lo_netmask(FAR struct net_driver_s *dev)
  214. {
  215. dev->d_ipv6netmask[0] = 0xffff;
  216. dev->d_ipv6netmask[1] = 0xffff;
  217. dev->d_ipv6netmask[2] = 0xffff;
  218. dev->d_ipv6netmask[3] = 0xffff;
  219. #if CONFIG_PKTRADIO_ADDRLEN == 1
  220. dev->d_ipv6netmask[4] = 0xffff;
  221. dev->d_ipv6netmask[5] = 0xffff;
  222. dev->d_ipv6netmask[6] = 0xffff;
  223. dev->d_ipv6netmask[7] = HTONS(0xff00);
  224. #elif CONFIG_PKTRADIO_ADDRLEN == 2
  225. dev->d_ipv6netmask[4] = 0xffff;
  226. dev->d_ipv6netmask[5] = 0xffff;
  227. dev->d_ipv6netmask[6] = 0xffff;
  228. dev->d_ipv6netmask[7] = 0;
  229. #elif CONFIG_PKTRADIO_ADDRLEN == 8
  230. dev->d_ipv6netmask[4] = 0;
  231. dev->d_ipv6netmask[5] = 0;
  232. dev->d_ipv6netmask[6] = 0;
  233. dev->d_ipv6netmask[7] = 0;
  234. #endif
  235. }
  236. /****************************************************************************
  237. * Name: lo_loopback
  238. *
  239. * Description:
  240. * Check if the network has any outgoing packets ready to send. This is
  241. * a callback from devif_poll() or devif_timer(). devif_poll() will be
  242. * called only during normal TX polling.
  243. *
  244. * Input Parameters:
  245. * dev - Reference to the NuttX driver state structure
  246. *
  247. * Returned Value:
  248. * OK on success; a negated errno on failure
  249. *
  250. * Assumptions:
  251. * May or may not be called from an interrupt handler. In either case,
  252. * the network is locked.
  253. *
  254. ****************************************************************************/
  255. static int lo_loopback(FAR struct net_driver_s *dev)
  256. {
  257. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private;
  258. struct pktradio_metadata_s pktmeta;
  259. FAR struct iob_s *iob;
  260. int ret;
  261. /* Create some fake metadata */
  262. memset(&pktmeta, 0, sizeof(struct pktradio_metadata_s));
  263. pktmeta.pm_src.pa_addrlen = CONFIG_PKTRADIO_ADDRLEN;
  264. pktmeta.pm_dest.pa_addrlen = CONFIG_PKTRADIO_ADDRLEN;
  265. /* On loopback the local address is both the source and destination. */
  266. memcpy(pktmeta.pm_src.pa_addr, g_mac_addr, CONFIG_PKTRADIO_ADDRLEN);
  267. memcpy(pktmeta.pm_dest.pa_addr, g_mac_addr, CONFIG_PKTRADIO_ADDRLEN);
  268. /* Loop while there framelist to be sent, i.e., while the freme list is not
  269. * emtpy. Sending, of course, just means relaying back through the network
  270. * for this driver.
  271. */
  272. while (priv->lo_head != NULL)
  273. {
  274. ninfo("Looping frame IOB %p\n", iob);
  275. /* Increment statistics */
  276. NETDEV_RXPACKETS(&priv->lo_radio.r_dev);
  277. /* Remove the IOB from the queue */
  278. iob = priv->lo_head;
  279. priv->lo_head = iob->io_flink;
  280. iob->io_flink = NULL;
  281. /* Did the framelist become empty? */
  282. if (priv->lo_head == NULL)
  283. {
  284. priv->lo_tail = NULL;
  285. }
  286. /* Make sure the our single packet buffer is attached */
  287. priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf;
  288. /* Return the next frame to the network */
  289. ninfo("Send frame %p to the network: Offset=%u Length=%u\n",
  290. iob, iob->io_offset, iob->io_len);
  291. ret = sixlowpan_input(&priv->lo_radio, iob, (FAR void *)&pktmeta);
  292. /* Increment statistics */
  293. NETDEV_TXPACKETS(&priv->lo_radio.r_dev);
  294. if (ret < 0)
  295. {
  296. nerr("ERROR: sixlowpan_input returned %d\n", ret);
  297. NETDEV_TXERRORS(&priv->lo_radio.r_dev);
  298. NETDEV_ERRORS(&priv->lo_radio.r_dev);
  299. }
  300. }
  301. return 0;
  302. }
  303. /****************************************************************************
  304. * Name: lo_loopback_work
  305. *
  306. * Description:
  307. * Perform loopback of received framelist.
  308. *
  309. * Input Parameters:
  310. * arg - The argument passed when work_queue() as called.
  311. *
  312. * Returned Value:
  313. * OK on success
  314. *
  315. * Assumptions:
  316. * The network is locked
  317. *
  318. ****************************************************************************/
  319. static void lo_loopback_work(FAR void *arg)
  320. {
  321. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg;
  322. /* Perform the loopback */
  323. net_lock();
  324. (void)lo_loopback(&priv->lo_radio.r_dev);
  325. net_unlock();
  326. }
  327. /****************************************************************************
  328. * Name: lo_poll_work
  329. *
  330. * Description:
  331. * Perform periodic polling from the worker thread
  332. *
  333. * Input Parameters:
  334. * arg - The argument passed when work_queue() as called.
  335. *
  336. * Returned Value:
  337. * OK on success
  338. *
  339. * Assumptions:
  340. * The network is locked
  341. *
  342. ****************************************************************************/
  343. static void lo_poll_work(FAR void *arg)
  344. {
  345. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg;
  346. /* Perform the poll */
  347. net_lock();
  348. #ifdef CONFIG_NET_6LOWPAN
  349. /* Make sure the our single packet buffer is attached */
  350. priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf;
  351. #endif
  352. /* And perform the poll */
  353. (void)devif_timer(&priv->lo_radio.r_dev, lo_loopback);
  354. /* Setup the watchdog poll timer again */
  355. (void)wd_start(priv->lo_polldog, LO_WDDELAY, lo_poll_expiry, 1, priv);
  356. net_unlock();
  357. }
  358. /****************************************************************************
  359. * Name: lo_poll_expiry
  360. *
  361. * Description:
  362. * Periodic timer handler. Called from the timer interrupt handler.
  363. *
  364. * Input Parameters:
  365. * argc - The number of available arguments
  366. * arg - The first argument
  367. *
  368. * Returned Value:
  369. * None
  370. *
  371. * Assumptions:
  372. * The network is locked.
  373. *
  374. ****************************************************************************/
  375. static void lo_poll_expiry(int argc, wdparm_t arg, ...)
  376. {
  377. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg;
  378. if (!work_available(&priv->lo_work) || priv->lo_head != NULL)
  379. {
  380. nwarn("WARNING: lo_work NOT available\n");
  381. priv->lo_pending = true;
  382. }
  383. else
  384. {
  385. /* Schedule to perform the interrupt processing on the worker thread. */
  386. priv->lo_pending = false;
  387. work_queue(LPBKWORK, &priv->lo_work, lo_poll_work, priv, 0);
  388. }
  389. }
  390. /****************************************************************************
  391. * Name: lo_ifup
  392. *
  393. * Description:
  394. * NuttX Callback: Bring up the Ethernet interface when an IP address is
  395. * provided
  396. *
  397. * Input Parameters:
  398. * dev - Reference to the NuttX driver state structure
  399. *
  400. * Returned Value:
  401. * None
  402. *
  403. * Assumptions:
  404. *
  405. ****************************************************************************/
  406. static int lo_ifup(FAR struct net_driver_s *dev)
  407. {
  408. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private;
  409. ninfo("Bringing up: IPv6 %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
  410. dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
  411. dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
  412. dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
  413. #if CONFIG_PKTRADIO_ADDRLEN == 1
  414. ninfo(" Node: %02x\n",
  415. dev->d_mac.radio.nv_addr[0]);
  416. #elif CONFIG_PKTRADIO_ADDRLEN == 2
  417. ninfo(" Node: %02x:%02x\n",
  418. dev->d_mac.radio.nv_addr[0], dev->d_mac.radio.nv_addr[1]);
  419. #elif CONFIG_PKTRADIO_ADDRLEN == 8
  420. ninfo(" Node: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x PANID=%02x:%02x\n",
  421. dev->d_mac.radio.nv_addr[0], dev->d_mac.radio.nv_addr[1],
  422. dev->d_mac.radio.nv_addr[2], dev->d_mac.radio.nv_addr[3],
  423. dev->d_mac.radio.nv_addr[4], dev->d_mac.radio.nv_addr[5],
  424. dev->d_mac.radio.nv_addr[6], dev->d_mac.radio.nv_addr[7]);
  425. #endif
  426. /* Set and activate a timer process */
  427. (void)wd_start(priv->lo_polldog, LO_WDDELAY, lo_poll_expiry,
  428. 1, (wdparm_t)priv);
  429. priv->lo_bifup = true;
  430. return OK;
  431. }
  432. /****************************************************************************
  433. * Name: lo_ifdown
  434. *
  435. * Description:
  436. * NuttX Callback: Stop the interface.
  437. *
  438. * Input Parameters:
  439. * dev - Reference to the NuttX driver state structure
  440. *
  441. * Returned Value:
  442. * None
  443. *
  444. * Assumptions:
  445. *
  446. ****************************************************************************/
  447. static int lo_ifdown(FAR struct net_driver_s *dev)
  448. {
  449. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private;
  450. ninfo("IP up: %u\n", priv->lo_bifup);
  451. /* Cancel the TX poll timer and TX timeout timers */
  452. wd_cancel(priv->lo_polldog);
  453. /* Mark the device "down" */
  454. priv->lo_bifup = false;
  455. return OK;
  456. }
  457. /****************************************************************************
  458. * Name: lo_txavail_work
  459. *
  460. * Description:
  461. * Perform an out-of-cycle poll on the worker thread.
  462. *
  463. * Input Parameters:
  464. * arg - Reference to the NuttX driver state structure (cast to void*)
  465. *
  466. * Returned Value:
  467. * None
  468. *
  469. * Assumptions:
  470. * Called on the higher priority worker thread.
  471. *
  472. ****************************************************************************/
  473. static void lo_txavail_work(FAR void *arg)
  474. {
  475. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg;
  476. ninfo("TX available work. IP up: %u\n", priv->lo_bifup);
  477. /* Ignore the notification if the interface is not yet up */
  478. net_lock();
  479. if (priv->lo_bifup)
  480. {
  481. /* If so, then poll the network for new XMIT data */
  482. #ifdef CONFIG_NET_6LOWPAN
  483. /* Make sure the our single packet buffer is attached */
  484. priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf;
  485. #endif
  486. /* Then perform the poll */
  487. (void)devif_poll(&priv->lo_radio.r_dev, lo_loopback);
  488. }
  489. net_unlock();
  490. }
  491. /****************************************************************************
  492. * Name: lo_txavail
  493. *
  494. * Description:
  495. * Driver callback invoked when new TX data is available. This is a
  496. * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
  497. * latency.
  498. *
  499. * Input Parameters:
  500. * dev - Reference to the NuttX driver state structure
  501. *
  502. * Returned Value:
  503. * None
  504. *
  505. * Assumptions:
  506. * Called in normal user mode
  507. *
  508. ****************************************************************************/
  509. static int lo_txavail(FAR struct net_driver_s *dev)
  510. {
  511. FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private;
  512. ninfo("Available: %u\n", work_available(&priv->lo_work));
  513. /* Is our single work structure available? It may not be if there are
  514. * pending actions and we will have to ignore the Tx availability
  515. * action.
  516. */
  517. if (!work_available(&priv->lo_work) || priv->lo_head != NULL)
  518. {
  519. nwarn("WARNING: lo_work NOT available\n");
  520. priv->lo_pending = true;
  521. }
  522. else
  523. {
  524. /* Schedule to perform the interrupt processing on the worker thread. */
  525. priv->lo_pending = false;
  526. work_queue(LPBKWORK, &priv->lo_work, lo_txavail_work, priv, 0);
  527. }
  528. return OK;
  529. }
  530. /****************************************************************************
  531. * Name: lo_addmac
  532. *
  533. * Description:
  534. * NuttX Callback: Add the specified MAC address to the hardware multicast
  535. * address filtering
  536. *
  537. * Input Parameters:
  538. * dev - Reference to the NuttX driver state structure
  539. * mac - The MAC address to be added
  540. *
  541. * Returned Value:
  542. * None
  543. *
  544. * Assumptions:
  545. *
  546. ****************************************************************************/
  547. #ifdef CONFIG_NET_MCASTGROUP
  548. static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
  549. {
  550. #if CONFIG_PKTRADIO_ADDRLEN == 1
  551. ninfo("MAC: %02x\n", mac[0]);
  552. #elif CONFIG_PKTRADIO_ADDRLEN == 2
  553. ninfo("MAC: %02x:%02x\n", mac[0], mac[1]);
  554. #elif CONFIG_PKTRADIO_ADDRLEN == 8
  555. ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
  556. mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]);
  557. #endif
  558. /* There is no multicast support in the loopback driver */
  559. return OK;
  560. }
  561. #endif
  562. /****************************************************************************
  563. * Name: lo_rmmac
  564. *
  565. * Description:
  566. * NuttX Callback: Remove the specified MAC address from the hardware multicast
  567. * address filtering
  568. *
  569. * Input Parameters:
  570. * dev - Reference to the NuttX driver state structure
  571. * mac - The MAC address to be removed
  572. *
  573. * Returned Value:
  574. * None
  575. *
  576. * Assumptions:
  577. *
  578. ****************************************************************************/
  579. #ifdef CONFIG_NET_MCASTGROUP
  580. static int lo_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
  581. {
  582. #if CONFIG_PKTRADIO_ADDRLEN == 1
  583. ninfo("MAC: %02x\n", mac[0]);
  584. #elif CONFIG_PKTRADIO_ADDRLEN == 2
  585. ninfo("MAC: %02x:%02x\n", mac[0], mac[1]);
  586. #elif CONFIG_PKTRADIO_ADDRLEN == 8
  587. ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
  588. mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]);
  589. #endif
  590. /* There is no multicast support in the loopback driver */
  591. return OK;
  592. }
  593. #endif
  594. /****************************************************************************
  595. * Name: macnet_ioctl
  596. *
  597. * Description:
  598. * Handle network IOCTL commands directed to this device.
  599. *
  600. * Input Parameters:
  601. * dev - Reference to the NuttX driver state structure
  602. * cmd - The IOCTL command
  603. * arg - The argument for the IOCTL command
  604. *
  605. * Returned Value:
  606. * OK on success; Negated errno on failure.
  607. *
  608. * Assumptions:
  609. *
  610. ****************************************************************************/
  611. #ifdef CONFIG_NETDEV_IOCTL
  612. static int lo_ioctl(FAR struct net_driver_s *dev, int cmd,
  613. unsigned long arg)
  614. {
  615. FAR struct pktradio_ifreq_s *cmddata;
  616. FAR struct lo_driver_s *priv;
  617. int ret = -ENOTTY;
  618. DEBUGASSERT(dev != NULL && dev->d_private != NULL && arg != 0ul);
  619. priv = (FAR struct lo_driver_s *)dev->d_private;
  620. cmddata = (FAR struct pktradio_ifreq_s *)((uintptr_t)arg);
  621. switch (cmd)
  622. {
  623. /* SIOCPKTRADIOGGPROPS
  624. * Description: Get the radio properties
  625. * Input: Pointer to read-write instance of struct
  626. * pktradio_ifreq_s
  627. * Output: Properties returned in struct pktradio_ifreq_s
  628. * instance
  629. */
  630. case SIOCPKTRADIOGGPROPS:
  631. {
  632. FAR struct radio_driver_s *radio =
  633. (FAR struct radio_driver_s *)dev;
  634. FAR struct radiodev_properties_s *props =
  635. (FAR struct radiodev_properties_s *)&cmddata->pifr_props;
  636. ret = lo_properties(radio, props);
  637. }
  638. break;
  639. /* SIOCPKTRADIOSNODE
  640. * Description: Set the radio node address
  641. * Input: Pointer to read-only instance of struct
  642. * pktradio_ifreq_s
  643. * Output: None
  644. */
  645. case SIOCPKTRADIOSNODE:
  646. {
  647. FAR const struct pktradio_addr_s *newaddr =
  648. (FAR const struct pktradio_addr_s *)&cmddata->pifr_hwaddr;
  649. if (newaddr->pa_addrlen != 1)
  650. {
  651. ret = -EINVAL;
  652. }
  653. else
  654. {
  655. FAR struct netdev_varaddr_s *devaddr = &dev->d_mac.radio;
  656. devaddr->nv_addrlen = 1;
  657. devaddr->nv_addr[0] = newaddr->pa_addr[0];
  658. #if CONFIG_PKTRADIO_ADDRLEN > 1
  659. memset(&devaddr->pa_addr[1], 0, CONFIG_PKTRADIO_ADDRLEN - 1);
  660. #endif
  661. ret = OK;
  662. }
  663. }
  664. break;
  665. /* SIOCPKTRADIOGNODE
  666. * Description: Get the radio node address
  667. * Input: Pointer to read-write instance of
  668. * struct pktradio_ifreq_s
  669. * Output: Node address return in struct pktradio_ifreq_s
  670. * instance
  671. */
  672. case SIOCPKTRADIOGNODE:
  673. {
  674. FAR struct pktradio_addr_s *retaddr =
  675. (FAR struct pktradio_addr_s *)&cmddata->pifr_hwaddr;
  676. FAR const struct netdev_varaddr_s *devaddr = &dev->d_mac.radio;
  677. retaddr->pa_addrlen = devaddr->nv_addrlen;
  678. retaddr->pa_addr[0] = devaddr->nv_addr[0];
  679. #if CONFIG_PKTRADIO_ADDRLEN > 1
  680. memset(&addr->pa_addr[1], 0, CONFIG_PKTRADIO_ADDRLEN - 1);
  681. #endif
  682. ret = OK;
  683. }
  684. break;
  685. default:
  686. wlwarn("WARNING: Unrecognized IOCTL command: %02x\n", cmd);
  687. break;
  688. }
  689. UNUSED(priv);
  690. return ret;
  691. }
  692. #endif
  693. /****************************************************************************
  694. * Name: lo_get_mhrlen
  695. *
  696. * Description:
  697. * Calculate the MAC header length given the frame meta-data.
  698. *
  699. * Input Parameters:
  700. * netdev - The networkd device that will mediate the MAC interface
  701. * meta - Obfuscated metadata structure needed to create the radio
  702. * MAC header
  703. *
  704. * Returned Value:
  705. * A non-negative MAC headeer length is returned on success; a negated
  706. * errno value is returned on any failure.
  707. *
  708. ****************************************************************************/
  709. static int lo_get_mhrlen(FAR struct radio_driver_s *netdev,
  710. FAR const void *meta)
  711. {
  712. return MAC_HDRLEN;
  713. }
  714. /****************************************************************************
  715. * Name: lo_req_data
  716. *
  717. * Description:
  718. * Requests the transfer of a list of frames to the MAC.
  719. *
  720. * Input Parameters:
  721. * netdev - The networkd device that will mediate the MAC interface
  722. * meta - Obfuscated metadata structure needed to create the radio
  723. * MAC header
  724. * framelist - Head of a list of frames to be transferred.
  725. *
  726. * Returned Value:
  727. * Zero (OK) returned on success; a negated errno value is returned on
  728. * any failure.
  729. *
  730. ****************************************************************************/
  731. static int lo_req_data(FAR struct radio_driver_s *netdev,
  732. FAR const void *meta, FAR struct iob_s *framelist)
  733. {
  734. FAR struct lo_driver_s *priv;
  735. FAR struct iob_s *iob;
  736. DEBUGASSERT(netdev != NULL && netdev->r_dev.d_private != NULL);
  737. priv = (FAR struct lo_driver_s *)netdev->r_dev.d_private;
  738. DEBUGASSERT(meta != NULL && framelist != NULL);
  739. /* Add the incoming list of framelist to queue of framelist to loopback */
  740. for (iob = framelist; iob != NULL; iob = framelist)
  741. {
  742. /* Increment statistics */
  743. NETDEV_RXPACKETS(&priv->lo_radio.r_dev);
  744. /* Remove the IOB from the queue */
  745. framelist = iob->io_flink;
  746. iob->io_flink = NULL;
  747. ninfo("Queuing frame IOB %p\n", iob);
  748. /* Just zero the MAC header for test purposes */
  749. DEBUGASSERT(iob->io_offset == MAC_HDRLEN);
  750. memset(iob->io_data, 0, MAC_HDRLEN);
  751. /* Add the IOB to the tail of the queue of framelist to be looped back */
  752. if (priv->lo_tail == NULL)
  753. {
  754. priv->lo_head = iob;
  755. }
  756. else
  757. {
  758. priv->lo_tail->io_flink = iob;
  759. }
  760. priv->lo_tail = iob;
  761. }
  762. /* Schedule to serialize the poll on the worker thread. */
  763. work_queue(LPBKWORK, &priv->lo_work, lo_loopback_work, priv, 0);
  764. return OK;
  765. }
  766. /****************************************************************************
  767. * Name: lo_properties
  768. *
  769. * Description:
  770. * Different packet radios may have different properties. If there are
  771. * multiple packet radios, then those properties have to be queried at
  772. * run time. This information is provided to the 6LoWPAN network via the
  773. * following structure.
  774. *
  775. * Input Parameters:
  776. * netdev - The network device to be queried
  777. * properties - Location where radio properities will be returned.
  778. *
  779. * Returned Value:
  780. * Zero (OK) returned on success; a negated errno value is returned on
  781. * any failure.
  782. *
  783. ****************************************************************************/
  784. static int lo_properties(FAR struct radio_driver_s *netdev,
  785. FAR struct radiodev_properties_s *properties)
  786. {
  787. DEBUGASSERT(netdev != NULL && properties != NULL);
  788. memset(properties, 0, sizeof(struct radiodev_properties_s));
  789. /* General */
  790. properties->sp_addrlen = CONFIG_PKTRADIO_ADDRLEN; /* Length of an address */
  791. properties->sp_framelen = CONFIG_IOB_BUFSIZE; /* Fixed frame length */
  792. /* Multicast address */
  793. properties->sp_mcast.nv_addrlen = CONFIG_PKTRADIO_ADDRLEN;
  794. memset(properties->sp_mcast.nv_addr, 0xee, RADIO_MAX_ADDRLEN);
  795. /* Broadcast address */
  796. properties->sp_bcast.nv_addrlen = CONFIG_PKTRADIO_ADDRLEN;
  797. memset(properties->sp_mcast.nv_addr, 0xff, RADIO_MAX_ADDRLEN);
  798. #ifdef CONFIG_NET_STARPOINT
  799. /* Star hub node address */
  800. properties->sp_hubnode.nv_addrlen = 1;
  801. properties->sp_hubnode.nv_addr[0] = CONFIG_SPIRIT_HUBNODE;
  802. #endif
  803. return OK;
  804. }
  805. /****************************************************************************
  806. * Public Functions
  807. ****************************************************************************/
  808. /****************************************************************************
  809. * Name: pktradio_loopback
  810. *
  811. * Description:
  812. * Initialize and register the Ieee802.15.4 MAC loopback network driver.
  813. *
  814. * Input Parameters:
  815. * None
  816. *
  817. * Returned Value:
  818. * OK on success; Negated errno on failure.
  819. *
  820. * Assumptions:
  821. *
  822. ****************************************************************************/
  823. int pktradio_loopback(void)
  824. {
  825. FAR struct lo_driver_s *priv;
  826. FAR struct radio_driver_s *radio;
  827. FAR struct net_driver_s *dev;
  828. ninfo("Initializing\n");
  829. /* Get the interface structure associated with this interface number. */
  830. priv = &g_loopback;
  831. /* Initialize the driver structure */
  832. memset(priv, 0, sizeof(struct lo_driver_s));
  833. radio = &priv->lo_radio;
  834. dev = &radio->r_dev;
  835. dev->d_ifup = lo_ifup; /* I/F up (new IP address) callback */
  836. dev->d_ifdown = lo_ifdown; /* I/F down callback */
  837. dev->d_txavail = lo_txavail; /* New TX data callback */
  838. #ifdef CONFIG_NET_MCASTGROUP
  839. dev->d_addmac = lo_addmac; /* Add multicast MAC address */
  840. dev->d_rmmac = lo_rmmac; /* Remove multicast MAC address */
  841. #endif
  842. #ifdef CONFIG_NETDEV_IOCTL
  843. dev->d_ioctl = lo_ioctl; /* Handle network IOCTL commands */
  844. #endif
  845. dev->d_private = (FAR void *)priv; /* Used to recover private state from dev */
  846. /* Set the network mask and advertise our MAC-based IP address */
  847. lo_netmask(dev);
  848. lo_addr2ip(dev);
  849. /* Initialize the Network frame-related callbacks */
  850. radio->r_get_mhrlen = lo_get_mhrlen; /* Get MAC header length */
  851. radio->r_req_data = lo_req_data; /* Enqueue frame for transmission */
  852. radio->r_properties = lo_properties; /* Returns radio properties */
  853. /* Create a watchdog for timing polling for and timing of transmissions */
  854. priv->lo_polldog = wd_create(); /* Create periodic poll timer */
  855. #ifdef CONFIG_NET_6LOWPAN
  856. /* Make sure the our single packet buffer is attached. We must do this before
  857. * registering the device since, once the device is registered, a packet may
  858. * be attempted to be forwarded and require the buffer.
  859. */
  860. priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf;
  861. #endif
  862. /* Register the loopabck device with the OS so that socket IOCTLs can be
  863. * performed.
  864. */
  865. (void)netdev_register(&priv->lo_radio.r_dev, NET_LL_PKTRADIO);
  866. /* Put the network in the UP state */
  867. dev->d_flags = IFF_UP;
  868. return lo_ifup(&priv->lo_radio.r_dev);
  869. }
  870. #endif /* CONFIG_PKTRADIO_LOOPBACK */