The device Structure in Detail

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 Visible Head

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 Hidden Fields

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.

Interface information

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 typefield is used by ARP to determine what kind of hardware address the interface supports. Ethernet interfaces set this to ARPHRD_ETHERether_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.

The device methods

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.

Utility fields

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.29.209