The device
structure is at the very core of network drivers
and deserves a complete description. At a first reading, however, you
can skip this section, because you don’t need a thorough understanding of
the structure to get started. This list describes all the fields,
but more to provide a reference rather than to be memorized.
The rest of this chapter briefly describes each field as soon
as it is used in the sample code, so you don’t need to keep referring back to
this section.
struct device
can be conceptually divided into two parts:
``visible'' and ``invisible.'' The visible part of the
structure is made up of the fields that are explicitly assigned in
static device
structures, like the two items appearing in
snull and shown above. The remaining fields are used
internally. Some are accessed by drivers (for example, the
ones that are assigned at initialization time), while some shouldn’t be
touched. This section is complete up to kernel version 2.0.30.
The first part of struct device
is composed of the following
fields, in this order:
char *name;
The name of the device. If the first character of the
name is zero (the NUL
character) or a
blank, register_netdev assigns it the name
eth
n
, with a suitable numeric
n.
unsigned long rmem_end;
,
unsigned long rmem_start;
,
unsigned long mem_end;
,
unsigned long mem_start;
These fields hold the beginning and ending addresses of
the shared memory used by the device. If the device has
different receive and transmit memory, the mem
fields
are used for transmit memory and the rmem
fields for receive
memory. mem_start
and mem_end
can be specified
on the kernel command line at system boot, and their value is
retrieved by ifconfig. The rmem
fields are never
referenced outside of the driver itself. By convention, the
end
fields are set so that end - start
is the
amount of available on-board memory.
unsigned long base_addr;
The I/O base address. This field, like the previous
ones, is assigned during device probe. The ifconfig
command can be used to display or modify the current value.
The base_addr
can be explicitly assigned on the kernel
command line at system boot or at load time.
unsigned char irq;
The assigned interrupt number. The value of
dev->irq
is printed by ifconfig when interfaces
are listed. This value can usually be set at boot or load time
and modified later using ifconfig.
unsigned char start;
,
unsigned char interrupt;
These fields are binary flags. start
is usually set
at device open and cleared at close, and it is non-zero
when the interface is ready to operate. interrupt
is
used to tell higher levels of code that an interrupt has arrived
for the interface and is being serviced.
unsigned long tbusy;
This field indicates ``Transmission Busy.'' It should be
non-zero whenever the driver can’t accept a new packet
for transmission (i.e., all of the output buffers are full).
A long
type is used instead of char
because atomic bit
operations are sometimes used to avoid race
conditions.
Note that in version 1.2 of the kernel, tbusy
was
indeed an 8-bit field, so a backward-portable driver should
take care of this issue. Atomic bit
operations were introduced in Section 9.7.3,
in Chapter 9.
struct device *next;
Used to maintain the linked list; this field shouldn’t be touched by the driver.
int (*init)(struct device *dev);
The initialization function. This field is usually the last
one explicitly listed in a device
structure.
The device
structure includes several additional fields, which
are usually assigned at device initialization. Some of these fields
convey information about the interface, while some exist only for the
benefit of the driver (i.e., they are not used by the kernel); there are
also other fields, most notably the device methods, that are part of
the kernel-driver interface.
I’m going to list the three groups separately, independent of the actual order of the fields, which is not significant.
Most of the information about the interface is correctly set up by
the function ether_setup. Ethernet cards can rely on this
general-purpose function for most of these fields, but the flags
and dev_addr
fields are device-specific and must be
explicitly assigned at initialization time.
Some non-Ethernet interfaces can use helper functions similar to
ether_setup. driver/net/net_init.c
exports tr_setup
(token ring) and fddi_setup. If your device doesn’t fall into
one of these classes, you’ll need to assign all of these fields by hand.
unsigned short hard_header_len;
The ``hardware header length'' is the number of octets
that lead the transmitted packet before the IP header,
or other protocol information. The
value of hard_header_len
is 14 for Ethernet
interfaces.
unsigned short mtu;
The ``maximum transfer unit.'' This field is used by the network layer during packet transmission. Ethernet has an MTU of 1500 octets.
__u32 tx_queue_len;
The maximum number of frames that can be queued on the device’s transmission queue. This value is set to 100 by ether_setup, but you can change it. For example, plip uses 10 to avoid wasting system memory (plip has a lower throughput than a real Ethernet interface).
unsigned short type;
The hardware type of the interface. The
type
field is used by ARP to determine what kind of
hardware address the interface supports. Ethernet interfaces
set this to
ARPHRD_ETHER
—ether_setup does
this for you.
unsigned char addr_len;
,
unsigned char broadcast[MAX_ADDR_LEN];
,
unsigned char dev_addr[MAX_ADDR_LEN];
The Ethernet address length is six octets (we are
referring to the hardware id of the interface board), and the
broadcast address is made up of six 0xff
octets;
ether_setup arranges for these values to be correct. The
device address, on the other hand, must be read from the
interface board in a device-specific way, and the driver should
copy it to dev_addr
. The hardware address is used to generate
correct Ethernet headers before the packet is handed over to the
driver for transmission. The snull device doesn’t use
a physical interface, and it invents its own hardware
address.
unsigned short family;
The address family for the interface, most often
AF_INET
. The interface doesn’t usually need to look at
this field or assign a value to it.
unsigned short pa_alen;
Protocol Address Length; set to four octets for
AF_INET
. The interface doesn’t need to modify this
number.
unsigned long pa_addr;
,
unsigned long pa_brdaddr;
,
unsigned long pa_mask;
The three addresses that characterize the interface:
the interface address, the broadcast address, and the
net mask. These values are protocol-specific (i.e., they are
``protocol addresses''), they are IP addresses if
dev->family
is AF_INET
. These fields are assigned
by ifconfig and are read-only for the driver.
unsigned long pa_dstaddr;
Point-to-point interfaces like plip and ppp use this field to record the IP number of the other side of the link. Like the previous fields, this field is read-only.
unsigned short flags;
Interface flags. The flags
field includes
the following bit values. The IFF_
prefix stands
for ``InterFace Flags.'' Some flags are managed by the kernel,
and some are set by the interface at initialization time to
assert various capabilities (or inabilities) of the
interface. The valid flags are:
IFF_UP
The kernel turns this flag on when the interface is active. The flag is read-only for the driver.
IFF_BROADCAST
This flag states that the broadcast address of the interface is valid. Ethernet boards support broadcast.
IFF_DEBUG
Debug mode. This flag can be used to control the verbosity
of your printk calls or for other debugging purposes.
Although no official driver currently uses this flag, it can be
set and reset by user
programs via ioctl, and your driver can use it.
The misc-progs/netifdebug
program can be used to turn the
flag on and off.
IFF_LOOPBACK
This flag should be set only in the loopback
interface. The kernel checks for IFF_LOOPBACK
instead of
hardwiring the lo
name as a special interface.
IFF_POINTOPOINT
The initialization function for point-to-point
interfaces should set this flag. For example, plip sets
it. The ifconfig utility can also set or clear the
flag. When IFF_POINTOPOINT
is set, dev->pa_dstaddr
should refer to the other end of the link.
IFF_NOARP
Conventional network interfaces can convey ARP packets. If the interface can’t perform ARP, it must set this flag. For example, point-to-point interfaces don’t need to run ARP, which would only convey additional traffic without retrieving useful information. snull runs without ARP capabilities, so it sets the flag.
IFF_PROMIS
This flag is set to get promiscuous operation. By default, Ethernet interfaces use a hardware filter to ensure that they receive only broadcast packets and packets directed to that interface’s hardware address. Packet sniffers like tcpdump set promiscuous mode on the interface in order to retrieve all packets that travel on the interface’s transmission medium.
IFF_MULTICAST
This flag is set by interfaces that are capable of
multicast transmission. ether_setup sets
IFF_MULTICAST
by default, so if your driver does
not support multicasting, it must clear the flag at
initialization time.
IFF_ALLMULTI
This flag tells the interface to receive all multicast
packets. The kernel sets it when the host performs multicast
routing, only if IFF_MULTICAST
is
set. IFF_ALLMULTI
is read-only for the
interface. Both IFF_MULTICAST
and
IFF_ALLMULTI
were defined as far back as 1.2, but
were unused at the time. We’ll see them used later in the
section Section 14.14.
IFF_MASTER
,
IFF_SLAVE
These flags are used by the load equalization code. The interface driver doesn’t need to know about them.
IFF_NOTRAILERS
,
IFF_RUNNING
These flags are unused in Linux, but exist for BSD compatibility.
When a program changes IFF_UP
, the open or close
device method is called. When IFF_UP
or any other flag is modified, the
set_multicast_list method is invoked. If the driver needs to
perform some action because of a modification in the flags, it must take
that action in set_multicast_list. For example, when
IFF_PROMIS
is set or reset, the on-board hardware filter
must be notified. The responsibilities of this device method are outlined
later in the section Section 14.14.
As happens with the char and block drivers, each network device
declares the functions that act on it. Operations that can be performed
on network interfaces are listed below. Some of the operations can be left
NULL
and some are usually untouched because
ether_setup assigns suitable methods to them.
Device methods for a network interface can be divided into two groups: fundamental and optional. Fundamental methods include those that are needed to be able to use the interface; optional methods implement more advanced functionalities that are not strictly required. The following are the fundamental methods:
int (*open)(struct device *dev);
Open the interface. The interface is opened whenever ifconfig activates it. The open method should register any system resource it needs (I/O ports, IRQ, DMA, etc.), turn on the hardware, and increment the module usage count.
int (*stop)(struct device *dev);
Stop the interface. The interface is stopped when it is brought down; operations performed at open time should be reversed.
int (*hard_start_xmit) (struct sk_buff *skb,
,
struct device *dev);
Hardware Start Transmission. This method requests
the transmission of a packet. The packet is contained in a
socket buffer (sk_buff
) structure. Socket buffers are
introduced below.
int (*rebuild_header) (void *buf, struct device *dev,
,
unsigned long raddr,
,
struct sk_buff *skb);
This function is used to rebuild the hardware header
before a packet is transmitted. The default function used by
Ethernet devices uses ARP to fill the packet with missing
information. The snull driver implements its own method
because ARP doesn’t run on sn
interfaces. (ARP is
explained later in this chapter.) The arguments to
rebuild_header are the pointer to the hardware header,
the device, the ``router address'' (the packet’s initial
destination), and the buffer being transmitted.
int (*hard_header) (struct sk_buff *skb, struct device *dev,
,
unsigned short type, void *daddr,
,
void *saddr, unsigned len);
Hardware Header. This function builds the hardware header from the source and destination hardware addresses that were previously retrieved; its job is to organize the information passed to it as arguments. eth_header is the default function for Ethernet-like interfaces, and ether_setup assigns this field accordingly. The order of the arguments shown applies to kernel 2.0 and later, while it was different in 1.2. This change is transparent to an Ethernet driver because it inherits the eth_header implementation; other drivers might want to deal with the difference to stay backward-compatible with 1.2.
struct enet_statistics* (*get_stats)(struct device *dev);
Whenever an application needs to get statistics for the interface, this method is called. This happens, for example, when ifconfig or netstat -i is run. A sample implementation for snull is introduced later, in Section 14.13.
int (*set_config)(struct device *dev, struct ifmap *map);
Change the interface configuration. This method is the entry point for configuring the driver. The I/O address for the device and its interrupt number can be changed at run time using set_config. This capability can be used by the system administrator if the interface cannot be probed for. This method is described later in Section 14.11.
The remaining device operations are those that I consider optional. The arguments passed to some of them changed several times during the transition from Linux 1.2 to Linux 2.0. If you are writing a driver that you want to work with both versions of the kernel, you might want to implement these operations only for versions starting with 2.0.
int (*do_ioctl) (struct device *dev, struct ifreq *ifr,
,
int cmd);
Perform interface-specific ioctl commands.
Implementation of those commands is described later in
Section 14.12. The prototype shown works
with all the kernels from 1.2 onward. The corresponding
field in struct device
can be left as NULL
if the interface doesn’t need any interface-specific
commands.
void (*set_multicast_list)(struct device *dev);
This method is called when the multicast list for the device changes and when the flags change. The argument passing was different in version 1.2. See Section 14.14 for further details and a sample implementation.
int (*set_mac_address)(struct device *dev, void *addr);
This function can be implemented if the interface supports the ability to change its hardware address. Most interfaces either don’t support this ability or use the default eth_mac_addr implementation. This prototype was different in version 1.2 as well.
#define HAVE_HEADER_CACHE
,
void (*header_cache_bind) (struct hh_cache **hhp,
,
struct device *dev,
,
unsigned short htype,
,
__u32 daddr);
,
void (*header_cache_update) (struct hh_cache *hh,
,
struct device *dev,
,
unsigned char * haddr);
These functions and the macro were missing in Linux 1.2.
Ethernet drivers don’t need to concern themselves with
header_cache
issues because eth_setup
arranges for default methods to be used.
#define HAVE_CHANGE_MTU
,
int (*change_mtu)(struct device *dev, int new_mtu);
This function is in charge of taking action if there is a change in the MTU (Maximum Transfer Unit) for the interface. Both the function and the macro were missing in Linux 1.2. If the driver needs to do anything particular when the MTU is changed, it should declare its own function, otherwise the default will do the right thing. snull has a template for the function if you are interested.
The remaining struct device
data fields are
used by the interface to hold useful status information. Some of the
fields are used by ifconfig and netstat to provide the user
with information about the current configuration. An interface should
thus assign values to these fields.
unsigned long trans_start;
,
unsigned long last_rx;
Both of these fields are meant to hold a value in
jiffies. They are currently unused, but the kernel might use
these timing hints in the future. The driver is responsible
for updating these values when transmission begins and when a
packet is received. The trans_start
field can also
be used by the driver to detect a lockup. The driver can check
for timeouts against trans_start
while waiting a
``transmission done'' interrupt.
void *priv;
The equivalent of filp->private_data
. The
driver owns this pointer and can use it at will. Usually the
private data structure includes a struct enet_statistics
item. The field was used earlier in Section 14.2.2.
unsigned char if_port;
This field is used to record which hardware port is being
used by the interface (e.g., BNC, AUI, TP). The if_port
field is for the
use of the driver, and any numerical value can be assigned
at will.
unsigned char dma;
The DMA channel being used by the device. This field
is used by the SIOCGIFMAP
ioctl command.
struct dev_mc_list *mc_list;
,
int mc_count;
These two fields are used in handling multicast
transmission. mc_count
is the count of items in
mc_list
. See Section 14.14 for further
details.
There are other fields in struct device
, but they are not used by
the driver.
3.16.29.209