O'Reilly logo

Linux Device Drivers, Second Edition by Alessandro Rubini, Jonathan Corbet

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Backward Compatibility

Version 2.3.43 of the kernel saw a major rework of the networking subsystem. The new “softnet” implementation was a great improvement in terms of performance and clean design. It also, of course, brought changes to the network driver interface—though fewer than one might have expected.

Differences in Linux 2.2

First of all, Linux 2.3.14 renamed the network device structure, which had always been struct device, to struct net_device. The new name is certainly more appropriate, since the structure was never meant to describe devices in general.

Prior to version 2.3.43, the functions netif_start_queue, netif_stop_queue, and netif_wake_queue did not exist. Packet transmission was, instead, controlled by three fields in the device structure, and sysdep.h implements the three functions using the three fields when compiling for 2.2 or 2.0.

unsigned char start;

This variable indicated that the interface was ready for operations; it was normally set to 1 in the driver’s open method. The current implementation is to call netif_start_queue instead.

unsigned long interrupt;

interrupt was used to indicate that the device was servicing an interrupt—accordingly, it was set to 1 at the beginning of the interrupt handler and to 0 before returning. It was never a substitute for proper locking, and its use has been replaced with internal spinlocks.

unsigned long tbusy;

When nonzero, this variable indicated that the device could handle no more outgoing packets. Where a 2.4 driver will call netif_stop_queue, older drivers would set tbusy to 1. Restarting the queue required setting tbusy back to 0 and calling mark_bh(NET_BH).

Normally, setting tbusy was sufficient to ensure that the driver’s hard_start_xmit method would not be called. However, if the networking system decided that a transmitter lockup must have occurred, it would call that method anyway. There was no tx_timeout method before softnet was integrated. Thus, pre-softnet drivers had to explicitly check for a call to hard_start_xmit when tbusy was set and react accordingly.

The type of the name field in struct device was different. The 2.2 version was simply

char *name;

Thus, the storage for the interface name had to be allocated separately, and name assigned to point to that storage. Usually the device name was stored in a static variable within the driver. The %d notation for dynamically assigned interface names was not present in 2.2; instead, if the name began with a null byte or a space character, the kernel would allocate the next eth name. The 2.4 kernel still implements this behavior, but its use is deprecated. Starting with 2.5, only the %d format is likely to be recognized.

The owner field (and the SET_MODULE_OWNER macro) were added in kernel 2.4.0-test11, just before the official stable release. Previously, network driver modules had to maintain their own use counts. sysdep.h defines an empty SET_MODULE_OWNER for kernels that do not have it; portable code should also continue to manage its use count manually (in addition to letting the networking system do it).

The link state functions (netif_carrier_on and netif_carrier_off) did not exist in the 2.2 kernel. The kernel simply did without that information in those days.

Further Differences in Linux 2.0

The 2.1 development series also saw its share of changes to the network driver interface. Most took the form of small changes to function prototypes, rather than sweeping changes to the network code as a whole.

Interface statistics were kept in a structure called struct 1enet_statistics, defined in <linux/if_ether.h>. Even non-Ethernet drivers used this structure. The field names were all the same as the current struct net_device_stats, but the rx_bytes and tx_bytes fields were not present.

The 2.0 kernel handled transmitter lockups in the same way as 2.2 did. There was, however, an additional function:

void dev_tint(struct device *dev);

This function would be called by the driver after a lockup had been cleared to restart the transmission of packets.

A couple of functions had different prototypes. dev_kfree_skb had a second, integer argument that was either FREE_READ for incoming packets (i.e., skbs allocated by the driver) or FREE_WRITE for outgoing packets (skbs allocated by the networking code). Almost all calls to dev_kfree_skb in network driver code used FREE_WRITE. The nonchecking versions of the skb functions (such as __skb_push) did not exist; sysdep.h in the sample code provides emulation for these functions under 2.0.

The rebuild_header method had a different set of arguments:

int (*rebuild_header) (void *eth, struct device *dev, 
unsigned long raddr, struct sk_buff *skb);

The Linux kernel also made heavier use of rebuild_header; it did most of the work that hard_header does now. When snull is compiled under Linux 2.0, it builds hardware headers as follows:

int snull_rebuild_header(void *buff, struct net_device *dev, unsigned long dst,
                    struct sk_buff *skb)
{
    struct ethhdr *eth = (struct ethhdr *)buff;

    memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
    memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
    eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
    return 0;
}

The device methods for header caching were also significantly different in this kernel. If your driver needs to implement these functions directly (very few do), and it also needs to work with the 2.0 kernel, see the definitions in <linux/netdevice.h> to see how things were done in those days.

Probing and HAVE_DEVLIST

If you look at the source for almost any network driver in the kernel, you will find some boilerplate that looks like this:

#ifdef HAVE_DEVLIST
/*
 * Support for an alternate probe manager,
 * which will eliminate the boilerplate below.
 */
struct netdev_entry netcard_drv =
{cardname, netcard_probe1, NETCARD_IO_EXTENT, netcard_portlist};
#else
/* Regular probe routine defined here */

Interestingly, this code has been around since the 1.1 development series, but we are still waiting for the promised alternate probe manager. It is probably safe to not worry about being prepared for this great change, especially since ideas for how to implement it will likely have changed in the intervening years.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required