Skip to content

How to implement a `ng_netdev` enabled device

Michael Richardson edited this page Sep 9, 2021 · 16 revisions

The following sections describe how to implement a ng_netdev enabled device.

required data types and variables

  • An instance of ng_netdev_driver_t that references your functions (as they are described below).
  • An extension of ng_netdev_t (an instance of this will be called the device descriptor)
    • You can extend a struct by adding the following members
      • ng_netdev_driver_t *driver (required, must be a pointer to your ng_netdev_driver_t instance above)
      • ng_netdev_event_cb_t event_cb (required)
      • kernel_pid_t mac_pid (required)
      • add additional members as you like (e.g. your configuration options or information of the states)

device initialization

  • Your device needs an initialization function which has the device instance as an out parameter
  • set driver to the ng_netdev_driver_t instance of your device
  • set your configuration options to there initial default value
    • if you have to initialize the hardware address, take it from a node-unique source like periph/cpuid.
    • remember to set the universal/local-bit for EUI-48 and EUI-64 to local!
    • remember to set the individual/group-bit for EUI-48 and EUI-64 to individual!
  • do any initialization routine your device needs

add event callback

  • (implementation of driver.add_event_callback)

  • the implementation is pretty much always the same:

     static int _add_cb(ng_netdev_t *dev, ng_netdev_event_cb_t cb)
     {
         if ((dev == NULL) || (dev->driver != &ng_tapnet_driver)) {
              return -ENODEV;
         }
    
         if (dev->event_cb != NULL) {
              return -ENOBUFS;
         }
    
         dev->event_cb = cb;
    
         return 0;
     }
    

remove event callback

  • (implementation of driver.rem_event_callback)

  • the implementation is pretty much always the same:

     static int _remove_cb(ng_netdev_t *dev, ng_netdev_event_cb_t cb)
     {
         if ((dev == NULL) || (dev->driver != &ng_tapnet_driver)) {
              return -ENODEV;
         }
    
         if (dev->event_cb != NULL) {
              return -ENOBUFS;
         }
    
         dev->event_cb = cb;
    
         return 0;
     }
    

events

  • in interrupt handler of driver:

    • determine event type event_type

      • value can be chosen by driver
      • not necessarily bound to ng_netdev_event_t
    • determine device dev

    • send message msg to dev->mac_pid (event handler thread) with

      msg.type == NG_NETDEV_MSG_TYPE_EVENT
      msg.content.value = event_type
      msg_send(&msg, dev->mac_pid);
      
  • in thread dev->mac_pid (dev is known by event handler thread):

    msg_receive(&msg);
    
    switch (msg.type) {
         case NG_NETDEV_MSG_TYPE_EVENT:
             dev->driver->isr_event(dev, msg.content.value);
             break;
         /* ... */
    }
    
  • in driver.isr_event(dev, event_type):

    • check dev (dev != NULL and dev->driver == &driver)
    • call event handlers _event_<event_type>() for event according to event
  • FAQ:

    • TODO

RX started

TODO

RX complete

  • read received data data (of length data_len) from device

  • add ng_netif_hdr_t with both source l2src (of length l2src_len) and destination address l2_dest (of length l2dst_len) to ng_pktbuf

    ng_pktsnip_t *hdr = ng_netif_hdr_build(l2src, l2src_len,
                                            l2dst, l2dst_len);
    
    if (hdr == NULL) {
         DEBUG("Packet buffer full.\n");
         return;
    }
    
    hdr->if_pid = thread_getpid();
    hdr->rssi = l2rssi;
    hdr->lqi = l2lqi;
    
  • determine demultiplexing type demux_type (e.g. via Ethertype with Ethernet devices) if available

  • otherwise demux_type can be a fixed value (e.g. NG_NETTYPE_SIXLOWPAN for IEEE 802.15.4 devices)

  • copy payload of received packet into packet buffer

    ng_nettype_t demux_type = NG_NETTYPE_UNDEF;
    
    switch (ether_type) {
    #ifdef MODULE_NG_IPV6
         case ETHER_TYPE_IPV6:
             demux_type = NG_NETTYPE_IPV6;
             break;
    #endif
    
         default:
             break;
    }
    
    pkt = ng_pktbuf_add(NULL, data + L2_HDR_LEN, data_len, demux_type);
    pkt->next = hdr;
    
    if (pkt == NULL) {
         DEBUG("Packet buffer full.\n");
         ng_pktbuf_release(hdr_snip);
         return;
    }
    
  • call callback event handler with pkt as argument if available:

    if (dev->event_cb) {
        dev->event_cb(NETDEV_EVENT_RX_COMPLETE, pkt);
    }
    else {
        ng_pktbuf_release(pkt);
    }
    
  • release packet also in other error case, no one else will clean-up your mess 😉

  • FAQ:

    • TODO

TX started

TODO

TX complete

TODO

send data

  • (implementation of driver.send_data)

  • check if pkt != NULL, return -EFAULT on failure

  • check dev (dev != NULL and dev->driver == &driver)

    • on failure: release pkt return -ENODEV
  • build device header from netif_hdr (is first header in pkt)

  • put device header in out buffer

  • put rest of pkt out buffer out_buf (may also be SPI buffer or similar)

    ng_pktsnip_t *ptr = pkt->next;
    int nwrite = L2_HDR_LEN;
    
    while (ptr) {
         memcpy(out_buf + nwrite, ptr->data, ptr->size);
    
         if (nwrite > OUT_BUF_SIZE) {
             ng_pktbuf_release(pkt);
             return -ENOBUFS;
         }
    
         ptr = ptr->next;
    }
    
    ng_pktbuf_release(pkt);
  • send content of out buffer

  • release packet also in other error case, no one else will clean-up your mess 😉

  • FAQ:

    • TODO

get

  • (implementation of driver.get)
  • check dev (dev != NULL and dev->driver == &driver), return -ENODEV on failure
  • check if getting of opt is supported, return -ENOSUP on failure
  • check if *value_len is appropriate for opt, return -EOVERFLOW on failure
    • use *value_len < sizeof(option_type) for array like options (like addresses)
    • use *value_len != sizeof(option_type) for primitive types
  • set *value_len to sizeof(option_type)
  • copy option_value into value
  • return length of parameter on success
  • FAQ:
    • TODO

set

  • (implementation of driver.set)
  • check dev (dev != NULL and dev->driver == &driver), return -ENODEV on failure
  • check if setting of opt is supported, return -ENOSUP on failure
  • check if *value_len is appropriate for opt, return -EOVERFLOW on failure
    • use *value_len < sizeof(option_type) for array like options (like addresses)
    • use *value_len != sizeof(option_type) for primitive types
  • set *value_len to sizeof(option_type)
  • copy option_value into value
  • return length of parameter on success
  • FAQ:
    • TODO
Clone this wiki locally