We’ll start looking at the structure of network drivers by dissecting
the snull source. Keeping the source code for several drivers
handy might help you follow the discussion. Personally, I suggest
loopback.c
, plip.c
, and 3c509.c
, in order of
increasing complexity. Keeping skeleton.c
handy
might help as well, although this sample driver doesn’t actually run.
All these files live in drivers/net
, within the
kernel source tree.
When a driver module is loaded into a running kernel, it requests resources and offers facilities; there’s nothing new in that. And there’s also nothing new in the way resources are requested. The driver should probe for its device and its hardware location (I/O ports and IRQ line)--but without registering them--as described in Section 9.2 in Chapter 9. The way a network driver is registered by its init_module function is different from char and block drivers. Instead of asking for a major number, the driver inserts a data structure for each newly detected interface into a global list of network devices.
Each interface is described by a struct device
item. The
structures for sn0
and sn1
, the two snull interfaces,
are declared like this:
char snull_names[16]; /* two eight-byte buffers */ struct device snull_devs[2] = { { snull_names, /* name--set at load time */ 0, 0, 0, 0, /* shmem addresses */ 0x000, /* ioport */ 0, /* irq line */ 0, 0, 0, /* various flags: init to 0 */ NULL, /* next ptr */ snull_init, /* init function, fill other fields with NULLs */ }, { snull_names+8,/* name--set at load time */ 0, 0, 0, 0, /* shmem addresses */ 0x000, /* ioport */ 0, /* irq line */ 0, 0, 0, /* various flags: init to 0 */ NULL, /* next ptr */ snull_init, /* init function, fill other fields with NULLs */ } };
Note that the first field, the name, points to a static buffer,
which will be filled at load time. In this way, the interface name can
be chosen later, as explained below. In case you are tempted to use an
explicit buffer in the structure, like "01234567"
, I warn you
that the code won’t work reliably. This is because the compiler collapses
duplicate strings; you end up with a single buffer and two
pointers to it. Moreover, the compiler could even choose to
store constant strings in read-only memory: not what you want.
I won’t completely describe struct device
until the
next section, because it is a huge structure, and it won’t help to
have it dissected so early. I prefer to use the structure in
the driver and explain each field as it is used.
The previous code fragment makes explicit use of the
name
and init
fields of struct device
. name
, the first struct device
field, holds the interface name (the string identifying the
interface). The driver can hardwire a name for the interface or it can
allow dynamic assignment, which works like this: if the first
character of the name is either the null character or a blank, device
registration uses the first available eth
n
name. Thus, the first Ethernet interface is called eth0
,
and the others follow in numeric order. The snull
interfaces are called sn0
and sn1
by
default. However, if eth=1
is specified at load time,
init_module uses dynamic assignment. The default names
are assigned by init_module:
if (!snull_eth) { /* call them "sn0" and "sn1" */ memcpy(snull_devs[0].name, "sn0", 4); memcpy(snull_devs[1].name, "sn1", 4); } else { /* use automatic assignment */ snull_devs[0].name[0] = snull_devs[1].name[0] = ' '; }
The init
field is a function pointer. Whenever you register
a device, the kernel asks the driver to initialize
itself. Initialization means probing for the physical interface and
filling the device
structure with the proper values, as described
in the following section. If initialization fails, the structure
is not linked to the global list of network devices. This peculiar
way of setting things up is most useful during system boot; every driver
tries to register its own devices, but only devices that exist are
linked to the list. This is different from char and block drivers, which
are organized as a two-level tree, indexed by major and minor numbers.
Since the real initialization is performed elsewhere, init_module has little to do, and a single statement does it:
for (i=0; i<2; i++) if ( (result = register_netdev(snull_devs + i)) ) printk("snull: error %i registering device "%s" ", result, snull_devs[i].name); else device_present++;
Probing for the device should be performed in the
init
function for the interface, which is often called the
``probe'' function. The single argument received by init
is
a pointer to the device being initialized, while its return value is
either 0 or a negative error code, usually -ENODEV
.
No real probing is performed for the snull interface, because it is not bound to any hardware. When you write a real driver for a real interface, the rules for probing char devices apply: check the I/O ports before using them and don’t write to them during the probe. Also, you should avoid registering I/O ports and interrupt lines at this point. Real registration should be delayed until device open time; this is particularly important if interrupt lines are shared with other devices. You don’t want your interface to be called every time another device triggers an IRQ line just to reply ``no, it’s not mine.''
Actually, device probing at load time is discouraged for ISA
devices because it is potentially dangerous--the ISA architecture is
notoriously fault-intolerant. For this reason, most network drivers
refuse to probe for their hardware when loaded as modules, and the
kernel proper probes only for the first network interface, without
performing any hardware tests after one network device has been
detected. Usually dev->base_addr
, the base I/O address for the
current device, determines what to do:
If dev->base_addr
is a valid
I/O address for the device, that value should be used
without probing any other I/O locations. This
happens, for example, when the value is assigned at load
time.
If dev->base_addr
is zero, probing for the
device is acceptable. A user can thus request a probe by setting
the I/O address to zero at load time.
Otherwise, no probing should be performed. The kernel
uses a value of 0xffe0
to prevent probing,
but any invalid address will do. It’s up to the driver to
silently reject invalid addresses in base_addr
. A
module should, by default, set the address to an impossible
value in order to prevent undesired probing. Note that looking
for PCI devices is always safe because it does not involve
probing (see Chapter 15).
As you may have noticed, this way of controlling probing using a load-time setting is the same technique that we used in skull.
On exit from dev->init
, the dev
structure should be
filled with correct values. Filling the structure is the main role
of the initialization routine. Fortunately, the kernel takes
care of some Ethernet-wide defaults, through the function
ether_setup, which fills struct device
.
The core of snull_init is:
ether_setup(dev); /* assign some of the fields */ dev->open = snull_open; dev->stop = snull_release; dev->set_config = snull_config; dev->hard_start_xmit = snull_tx; dev->do_ioctl = snull_ioctl; dev->get_stats = snull_stats; dev->rebuild_header = snull_rebuild_header; /* keep the default flags, just add NOARP */ dev->flags |= IFF_NOARP;
The single unusual feature of the code is setting IFF_NOARP
in the flags. This specifies that the interface cannot
use ARP, the ``Address Resolution Protocol.'' ARP is a low-level Ethernet
protocol; every real Ethernet interface is ARP-aware and therefore won’t
set this flag. It’s interesting to note that an interface can work
without ARP. For example, the plip interface is an Ethernet-like
interface without ARP support, like snull. This topic is discussed
in detail later in Section 14.9, while the
device
structure is dissected in the next section.
I’d like to introduce now another struct device
field, priv
. Its role is similar to that of the
private_data
pointer that we used for char drivers. Unlike
fops->private_data
, this priv
pointer is
allocated at initialization time, instead of open time, because the data
item pointed to by priv
includes the statistics for the
interface. It’s important that statistical information is always available,
even when the interface is down, because users may want to display
the statistics at any time by calling ifconfig. The memory wasted
by allocating priv
during initialization instead of
on open is irrelevant because most probed interfaces are
constantly up and running in the system. The snull module
declares a snull_priv
data structure to be used for
priv
. The structure includes struct enet_statistics
, which
is the standard place to hold interface statistics.
The following lines in snull_init allocate dev->priv
:
dev->priv = kmalloc(sizeof(struct snull_priv), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct snull_priv));
Nothing special happens when the module is unloaded. The cleanup_module function simply unregisters the interfaces from the list, after releasing memory associated with the private structure:
void cleanup_module(void) { int i; for (i=0; i<2; i++) { kfree(snull_devs[i].priv); unregister_netdev(snull_devs + i); } return; }
While there is no notable difference between modularized and non-modularized char and block drivers, that’s not the case for network drivers.
When a driver is distributed as part of the mainstream Linux kernel,
it doesn’t declare its own device
structures; the structures
declared in drivers/net/Space.c
are used instead.
Space.c
declares a linked list of all the network devices, both
driver-specific structures like plip1
and general-purpose
eth
devices. Ethernet drivers don’t care about their
device
structures at all, because they use the general-purpose
structures. Such general eth
device structures declare
ethif_probe as their init
function. A programmer
inserting a new Ethernet interface in the mainstream kernel only needs
to add a call to the driver’s initialization function to
ethif_probe. Authors of non-eth
drivers, on the other hand,
insert their device
structures in Space.c
. In both cases
only the source file
Space.c
has to be modified if the driver must be linked to
the kernel proper.
At system boot, the network initialization code loops through all
the device
structures and calls their probing
(dev->init
) functions by passing them a pointer to the device
itself. If the probe function succeeds, Space.c
initializes
the device
structure. This way of setting up drivers permits
incremental assignment of devices to the names
eth0
, eth1
, and so on,
without changing the name
field of each device.
When a modularized driver is loaded, on the other hand, it declares
its own device
structures (as we have seen in this chapter), even
if the interface it controls is an Ethernet interface.
The curious reader can learn more about interface initialization
by looking at Space.c
and net_init.c
. This introduction
to driver setup is meant only to stress the importance of the
init
device method. If a driver module were to contain a pre-filled
device structure, it wouldn’t fit the initialization technique of the
mainstream kernel and wouldn’t be forward-compatible if some new
field were introduced in struct device
.
52.14.126.74