The HyperNews Linux KHG Discussion Pages

## network driver info

  Forum: Device Drivers
  Re: Re: Network Device Drivers (Paul Gortmaker)
  Re: Re: Network Device Drivers (Neal Tucker)
  Keywords: network driver functions
  Date: Sat, 15 Jun 1996 03:33:21 GMT
  From: Neal Tucker <ntucker@adobe.com>

Earlier, I posted a pointer to a bit of info on network device drivers, and the site that the web page is on is going away, so I am including what was there here... #### How a Network Device Gets Added to the Kernel There is a global variable called `dev_base` which points to a linked list of "device" structures. Each record represents a network device, and contains a pointer to the device driver's initialization function. The initialization function is the first code from the driver to ever get executed, and is responsible for setting up the hooks to the other driver code. At boot time, the function `device_setup` (drivers/block/genhd.c) calls a function called `net_dev_init` (net/core/dev.c) which walks through the linked list pointed to by `dev_base`, calling each device's `init` function. If the `init` indicates failure (by returning a nonzero result), `net_dev_init` removes the device from the linked list and continues on. This brings up the question of how the devices get added to the linked list of devices before any of their code is executed. That is accomplished by a clever piece of C preprocessor work in drivers/net/Space.c. This file has the static declarations for each device's "device" struct, including the pointer to the next device in the list. How can we define these links statically without knowing which devices are going to be included? Here's how it's done (from drivers/net/Space.c):
        #define NEXT_DEV        NULL

        #if defined(CONFIG_SLIP)
        static struct device slip_dev =
        {
          device name and some other info goes here
          ...
          NEXT_DEV,    /* <- link to previously listed */
                       /*    device struct (NULL here) */
          slip_init,   /* <- pointer to init function */
        };

        #undef NEXT_DEV
        #define NEXT_DEV (&slip_dev)
        #endif

        #if defined(CONFIG_PPP)
        static struct device ppp_dev =
        {
          device name and some other info goes here
          ...
          NEXT_DEV,   /* <- link to previously listed   */
                      /*    device struct, which is now *
                      /*    defined as &slip_dev        */
          ppp_init,   /* <- pointer to init function */
        };

        #undef NEXT_DEV
        #define NEXT_DEV (&ppp_dev)
        #endif

        struct device loopback_dev =
        {
          device name and some other info goes here
          ...
          NEXT_DEV,        /* <- link to previously listed   */
                           /*    device struct, which is now */
                           /*    defined as &ppp_dev         */
          loopback_init,   /* <- pointer to init function */
        };

        /* And finally, the head of the list, which points   */
        /* to the most recently defined device struct,       */
        /* loopback_dev.  This (dev_base) is the pointer the */
        /* kernel uses to access all the devices.            */

        struct device *dev_base = &loopback_dev;
There is a constant, `NEXT_DEV`, defined to always point at the last device record declared. When each device record gets declared, it puts the value of `NEXT_DEV` in itself as the "next" pointer and then redefines `NEXT_DEV` to point to itself. This is how the linked list is built. Note that `NEXT_DEV` starts out `NULL` so that the first device structure is the end of the list, and at the end, the global `dev_base`, which is the head of the list, gets the value of the last device structure. ##### Ethernet devices Ethernet devices are a bit of a special case in how they get called at initialization time, probably due to the fact that there are so many different types of ethernet devices that we'd like to be able to refer to them by just calling them ethernet devices (ie "eth0", "eth1", etc), rather than calling them by name (ie "NE2000", "3C509", etc). In the linked list mentioned above, there is a single entry for all ethernet devices, whose initialization function is set to the function `ethif_probe` (also defined in drivers/net/Space.c). This function simply calls each ethernet device's `init` function until it finds one that succeeds. This is done with a huge expression made up of the ANDed results of the calls to the initialization functions (note that with the ethernet devices, the init function is conventionally called `xxx_probe`). Here is an abridged version of that function:
        static int ethif_probe(struct device *dev)
        {
            u_long base_addr = dev->base_addr;

            if ((base_addr == 0xffe0)  ||  (base_addr == 1))
                return 1;

            if (1                  /* note start of expression here */
        #ifdef CONFIG_DGRS
                && dgrs_probe(dev)
        #endif
        #ifdef CONFIG_VORTEX
                && tc59x_probe(dev)
        #endif
        #ifdef CONFIG_NE2000
                && ne_probe(dev)
        #endif
                && 1 ) {          /* end of expression here         */
                return 1;
            }
            return 0;
        }
The result is that the if statement bails out as false if any of the `probe` calls returns zero (success), and only one ethernet card is initialized and used, no matter how many drivers you have installed. For the drivers that aren't installed, the `#ifdef` removes the code completely, and the expression gets a bit smaller. The implications of this scheme are that supporting multiple ethernet cards is now a special case, and requires providing command line parameters to the kernel which cause `ethif_probe` to be executed multiple times.

Messages

1. Network Driver Desprately Needed by Paul Atkinson