pktradio_loopback.c 31 KB

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