Initialization

If we look at trunk/sys/dev_qnx/speedo/speedo.c, we can see a function speedo_entry(), which io-pkt calls when the DLL devnp-speedo.so is first loaded. It in turn simply calls speedo_detect(), which is located in trunk/sys/dev_qnx/speedo/detect.c.

The speedo_detect() function does the PCI bus scanning, and for each matching VID/DID, it calls back up into io-pkt via the dev_attach() function. The io-pkt manager then calls back down into the driver's speedo_attach() function, which allocates driver resources.

From an io-pkt driver's perspective, initialization is quite different from that with io-net: it takes places in two phases: *_attach(), then *_init(). In the example above, speedo_attach() is immediately called, and then after an indeterminate period of time — e.g. when ifconfig assigns an IP address, or when Qnet is started — speedo_init() is called.

And here's the curve ball: speedo_init() may be called over and over and over again. For example, if you run the ifconfig utility twice, with two different IP addresses, speedo_init() will be called twice!

Note: When you're writing an io-pkt driver, you must be very aware that your driver's *_init() function will be called over and over again, so it should preferably not allocate any resources — if it does, it must do so carefully to make sure it doesn't do so improperly the second and third times through.

Back to speedo_attach() in detect.c. Note that it creates and fills in an io-net-style nic_config_t structure, which is used by the mii code and for making the nicinfo utility work nicely.

The speedo_attach() function also does the usual interfacing to the hardware, pretty much as the io-net driver does. However, it does make an io-pkt-specific call to interrupt_entry_init(), which is worth mentioning.

In io-pkt, interrupt handling and thread control is done by the io-pkt framework, not by the driver, for maximum performance.

To that end, the driver doesn't directly set up an interrupt handler; rather, it asks io-pkt to take the interrupt, which then calls into the driver's process_interrupt() function discussed below in the "Receive" section.

At the end of speedo_attach(), note the calls into io-pkt:

if_attach()
ether_ifattach()

which are required to register the io-pkt driver.

Now let's take a look at the second stage of initialization, which occurs in speedo_init() in detect.c. Remember that this function can be called over and over again at any time by io-pkt! The io-pkt manager actually learns about this call via the ifp structure that was passed to io-pkt in the if_attach() call above.

The first thing speedo_init() does is check to see if it's dying (see "Shutdown," below) and if so, it immediately returns. Shutdown is non-trivial.

The next thing speedo_init() does is call speedo_stop(), which also can be called directly by io-pkt. The speedo_stop() function, as the name hints at, halts the hardware but doesn't free resources allocated in speedo_attach().

Then, the NIC is reinitialized (e.g. descriptors, etc). Note that InterruptAttach() is called only if speedo->iid is invalid.

Finally, flags are set in the ifp structure to indicate that the driver is active. This is important.