Sample network drivers

ure sample driver

The ure driver project illustrates the additional changes needed to compile a FreeBSD driver into an io-sock driver. The project is available at https://gitlab.com/qnx/projects/drivers/ure.
Note:
The ure driver is also included with QNX SDP 8.0.3; it's available as the devs-ure.so shared library in the /lib/dll directory for each specific target architecture .

To set up and compile the project, follow these steps:

  1. Copy the driver code in the following files from FreeBSD source project to the QNX ure sample driver project:
    • if_ure.c
    • if_urereg.h
  2. Modify if_ure.c. For more information, refer to the Writing Network Drivers for io-sock section in the High-Performance Networking Stack (io-sock) User's Guide.
    1. Add the driver version and register it.
      The code below:
      • Declares and initializes a driver structure.
      • Adds a pointer to the driver structure from the set.sysctl_set linker set.
      • Declares and initializes the io-sock module version.
      • Declares, defines, and specifies an empty cleanup function, which is required but never actually called in this case.
      ...
      #ifdef __QNXNTO__
      int drvr_ver = IOSOCK_VERSION_CUR;
      SYSCTL_INT(_qnx_driver, OID_AUTO, ure, CTLFLAG_RD, &drvr_ver, 0,
          "Version");
      
      struct _iosock_module_version iosock_module_version =
          IOSOCK_MODULE_VER_SYM_INIT;
      
      static void
      ure_uninit(void *arg)
      {
      }
      /*
       * If code is added to ure_uninit, then SI_SUB_DUMMY needs to be
       * changed to SI_SUB_DRIVERS.  With SI_SUB_DUMMY, ure_uninit does
       * not get called.
       */
      SYSUNINIT(ure_uninit, SI_SUB_DUMMY, SI_ORDER_ANY, ure_uninit, NULL);
      #endif /* __QNXNTO__ */
      ...
    2. Locate the required header file if_urereg.h that's included with the source and add the qnx/qnx_modload.h header file:
      ...
      #include <sys/socket.h>
      #include <sys/sysctl.h>
      #ifndef __QNXNTO__
      #include <sys/unistd.h>
      #endif /* !__QNXNTO__ */
      
      #include <net/if.h>
      #include <net/if_var.h>
      ...
      #include <dev/usb/usb_process.h>
      
      #include <dev/usb/net/usb_ethernet.h>
      #ifndef __QNXNTO__
      #include <dev/usb/net/if_urereg.h>
      #else /* !__QNXNTO__ */
      #include "if_urereg.h"
      #include <qnx/qnx_modload.h>
      #endif /* __QNXNTO__ */
      
      #include "miibus_if.h"
      ...
  3. Add a Makefile to compile the code by doing one of the following:
    • Use the recursive makefile, as described in the Modifying the makefile section of the High-Performance Networking Stack (io-sock) User's Guide.
    • Ensure that your build specifies appropriate macros (__EXT, _KERNEL, _UNISTD_H_INCLUDED), options (-fno-builtin-log), and include paths in the ${QNX_TARGET} directory (/usr/include/devs, /usr/include/devs/contrib/ck/include, /usr/include/devs/include_aarch64, /usr/include/devs/qnx-gen, and /usr/include/devs/sys-nto).
    You must also pass the standard shared library build options. For example:
    qcc -V gcc_ntoaarch64 -D__EXT -D_KERNEL -D_UNISTD_H_INCLUDED -fno-builtin-log -I${QNX_TARGET}/usr/include/devs -I${QNX_TARGET}/usr/include/devs/contrib/ck/include -I${QNX_TARGET}/usr/include/devs/include_aarch64 -I${QNX_TARGET}/usr/include/devs/qnx-gen -I${QNX_TARGET}/usr/include/devs/sys-nto -shared -Wl,-hdevs-ure.so -EL -o devs-ure.so if_ure.c

Hardware-independent sample driver

You can find another sample network driver in the A Hardware-Independent Sample Driver section of the High-Performance Networking Stack (io-sock) User's Guide. This driver doesn't provide any meaningful functionality because the devices it expects do not exist; however, it does implement the structure of a real driver.

Comparing sample network drivers

Both the ure and hardware-independent sample drivers:
  • Use the SYSCTL_INT macro to add the drivers and register them in the set.sysctl_set.
  • Declare and initialize an io-sock module version.
  • Declare, define, and specify an empty cleanup function.
  • Use the MODULE_DEPEND macro to set the driver module dependencies.
  • Use the MODULE_VERSION macro to set the kernel module version.

The following table highlights the differences between these drivers:

ure driver Hardware-independent driver
The STRUCT_USB_HOST_ID ure_devs structure defines vendor and product IDs that this driver services. The struct ofw_compat_data compat_data structure defines the devices that this driver services.

The device_method_t ure_methods structure defines the device-specific callback functions:

int ure_probe(device_t dev)
Probe for devices specified in ure_devs.
int ure_attach(device_t dev)
Attach the interface, allocate memory, and perform ifmedia setup and Ethernet attach.
int ure_detach(device_t dev)
Detach the interface.
int ure_miibus_readreg(device_t dev, int phy, int reg)
Synchronously read registers.
int ure_miibus_writereg(device_t dev, int phy, int reg, int val)
Synchronously write registers.
void ure_miibus_statchg(device_t)
Synchronous callback; called by the media-independent interface (MII) bus driver when the physical layer establishes a link.
The device_method_t sam_methods structure defines the device-specific callback functions:
int sam_probe(device_t dev)
Probe for devices specified in compat_data.
int sam_attach(device_t dev)
Attach the interface, allocate memory, and perform ifmedia setup and ethernet attach.
int sam_detach(device_t dev)
Detach the interface.
int sam_shutdown(device_t dev)
Put hardware in a safe state as quickly as possible (not a graceful stop).
int sam_miibus_readreg(device_t dev, int phy, int reg)
Synchronously read registers. Used for both miibus_readreg and etherswitch_readphyreg callbacks.
int sam_miibus_writereg(device_t dev, int phy, int reg, int val)
Synchronously write registers.
int sam_miibus_readextreg(device_t dev, int phyad, int devad, int regad)
Synchronously read extended registers.
int sam_miibus_writeextreg(device_t dev, int phyad, int devad, int regad, int data)
Synchronously write extended registers.
void sam_miibus_statchg(device_t)
Synchronous callback; called by the MII bus driver when the physical layer establishes a link.
etherswitch_info_t* sam_es_getinfo(device_t dev)
Get the capability and configuration of the etherswitch layer.
int sam_es_readreg(device_t dev, int reg)
Read a register from an Ethernet switch device.
The driver_t ure_driver structure defines the driver name and connects the methods that the driver implements. The driver_t sample_driver structure defines the driver name and connects the methods that the driver implements.
The DRIVER_MODULE macro declares the driver as the miibus and ure driver. The DRIVER_MODULE macro declares the driver as the miibus, sample, and etherswitch driver.
The USB_PNP_HOST_INFO macro registers a table of USB device-identifying data. No direct comparison.
The struct usb_ether_methods ure_ue_methods structure defines the callback functions used to manage and interact with USB device:
void ure_attach_post(struct usb_ether *ue)
Primary post-attachment callback, called before network interface is attached and operational.
int ure_attach_post_sub(struct usb_ether *ue)
Secondary post-attachment callback, called after network interface is fully functional.
void ure_init(struct usb_ether *ue)
Initialize the device and its data structures.
void ure_start(struct usb_ether *ue)
Start the device's network operations.
void ure_stop(struct usb_ether *ue)
Stop the device's network operations.
void ure_tick(struct usb_ether *ue)
Periodic callback mechanism.
void ure_rxfilter(struct usb_ether *ue)
Configure hardware to handle multicast and unfiltered traffic. Used for both ue_setmulti and ue_setpromisc callbacks.
int ure_ifmedia_upd(struct ifnet *ifp)
Update MII settings.
void ure_ifmeda_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
Retrieve the current status of the MII.
No direct comparison.
Driver defines other device specific helper methods called by the device-specific callback and USB Ethernet callback methods. No direct comparison.
Page updated: