Writing Linux Device Drivers

Following are the notes that I had taken, while learning about driver programming from Rubini’s Linux Device Drivers, Third Edition. It also contains some of my own explorations into driver programming.

Writing Network Drivers

Device Registration

  • On loading, the driver module probes to find out ethernet devices, their IO locations and interrupts. But does not register them.
  • A data structure(struct net_device) is allocated for each interface.
  • The allocation is done using
struct net_device *alloc_netdev(int sizeof_priv, 
                                const char *name,
				void (*setup)(struct net_device *));
  • sizeof_priv is the size of the driver’s private data.
  • The private data is allocated along with net_device.
  • name is the name of the interface, such as eth0. It can contain a %d, which gets replaced by the next available interface no.
  • setup points to the intialization function that sets up the rest of the net_device structure.
  • If allocation fails ‘alloc_netdev’ returns NULL.
  • The alloc_etherdev is a wrapper to alloc_netdev that takes care of intializing net_device for an ethernet interface.
struct net_device *alloc_etherdev(int sizeof_priv);
  • In this case, any other required initialization can be done, after successful, allocation of the net_device structure.
  • There are similar wrapper for other link layer protocols.
  • After the initializing the net_device structure it has to be registered using
int register_netdev(struct net_device *);
  • As soon as the register function succeeds the callback functions can get called, so the network device has to completely initalized before calling this function.
  • Some of the important fields of the net_device structure are open, stop, set_config, hard_start_xmit, do_ioctl, get_stats, rebuild_header, hard_header, tx_timeout, watchdog_timeo, flags, features, hard_header_cache.
  • The sturcture also has private date pointer that should be accessed using
void *netdev_priv(struct net_device *);

Module Unloading

  • Unregister the interface, and perform other cleanup, and free the net_device structure.
  • The interface is unregistered using
void unregister_netdev(struct net_device *);
  • The net_device is freed using
void free_netdev(struct net_device *);
  • The private area also gets deallocated. So any access to the net_device structure or the private data should be done before calling free_netdev.

net_device Structure

  • Global information
    • name - the name of the iterface, like eth0
    • state - flags for the device state. Are manipulated using helper functions.
    • next - pointer to next net_device structure in the global linked list.
    • init - not used any more.
  • Hardware information
    • The net_device structure contains some useless information, only used by older drivers.
  • Interface information
    • ether_setup, sets up most of the required field.
  • But the flags and dev_addr fields require explicit initialization.
  • The fields that are set include
    • hard_header_len - the length of the link layer header.
    • mtu - size of the payload - exluding the link layer header.
    • tx_queue_len - set to 1000 by ether_setup.
    • type - used by ARP to determine the type of hardware address. Set to ARPHRD_ETHER by ether_setup.
    • addr_len - hardware address len.
    • broadcast - broadcast address string.
    • dev_addr - hardware address of the device. Used to generate

correct ethernet headers.

  • flags - bit mask. Bit values start with IFF_ prefix, and stand for InterFace Flags.
Flag Driver R/W Meaning
IFF_UP R Interface is active and ready to transmit packets.
IFF_BROADCAST R Interface allows broadcasting.
IFF_DEBUG R Activate debug. Can be set through ioctl.
IFF_LOOPBACK R/W Inteface is a loopback interface.
IFF_POINTOPOINT R/W Indicates interface is connected to P-to-P link.
IFF_NOARP R/W Interface cannot perform ARP.
IFF_PROMISC R Activate promiscus operation.
IFF_MULTICAST R/W Interface allows multicast transmission. Set by ether_setup
IFF_ALLMULTI R Receive all multicast packets.

  • features - special hardware capablities the interface has
Feature Meaning
NETIF_F_SG Interface can perform scatter/gather IO.
NETIF_F_FRAGLIST Interface can cope with packets that have been fragmented???
NETIF_F_IP_SUM Interface can calculate checksums for IP packets.
NETIF_F_NO_SUM No checksums are required by the interface. Set by loopback.
NETIF_F_HW_SUM Hardware calculates link layer checksum.
NETIF_F_HIGHDMA Device can perform DMA to high memory.
NETIF_F_HW_VLAN_* FIXME What is VLAN?
NETIF_F_TSO Device can perform TCP segmentation off loading???

  • Device Methods
    • Some of the device methods is setup by ether_setup.
    • Fundamental device methods
      • open - called when ifconfig activates it. Should register system resources like I/O ports, IRQ, etc.
      • stop - called when ifconfig deactiavtes it. Should free acquired resources.
      • hard_start_xmit - initiates the transmission of a packet. Packet is passed to the function.
      • hard_header - build link layer header from passed information. Set by ether_setup to eth_header.
      • rebuild_header - rebuild link layer header after ARP completes. Set by ether_setup.
      • tx_timeout - called when transmission fails to complete within reasonable period.
      • get_stats - called to get statistics for the interface.(ifconfig, netstat)
      • set_config - called to change interface configuration. Ex: IO address, IRQ. Mordern hardware drivers need not implement this method.
    • Advance device methods
      • poll - provided to operate the interface in polled mode(interrupts disabled). (NAPI)
      • poll_controller - Check interface for events when interrupts are disabled.
      • do_ioctl - Perform interface specific ioctl.
      • set_multicast_list -
      • set_mac_address - Can be impletemted if the device supports it. Can be set to eth_mac_addr function, which sets the dev→dev_addr field only if the interface is down. The MAC address in dev→dev_addr should be update to the hardware in the open method.
      • change_mtu - Take any required actions for MTU change. Default does the right thing.
      • header_cache - Called to fill hh_cache structure with the result of an ARP query. Set to eth_header_cache by ether_setup???
      • header_cache_update - Update destination address in hh_cache structure. Set to eth_header_cache_update by ether_setup???
      • hard_header_parse - Extract source address from packet. Set to eth_header_parse by ether_setup???
    • Utility Fields - hold status information. Used by ifconfig and netstat.
      • trans_start, last_rx - jiffies of beginning of last transmission, jiffies of last packet reception. trans_start used to detect transmitter lockups.
      • wathdog_timeo - min time in jiffies between trasmission timout and call of tx_timeout function.
      • priv - hold private data.
      • mc_list, mc_count - handle multicast transmission.
      • xmit_lock, xmit_lock_owner - serialize calls to hard_start_transmit. lock owner is the CPU holding the lock.

Opening and Closing

  • The interface must be opened and assigned an address before it can carry packets. This is done in response to ifconfig commands.
  • When an address is assigned using ifconfig,
    • Address is set using an ioctl. The ioctl is handled by the kernel
    • IFF_UP bit is set and open is invoked using another ioctl (also handled by the kernel).
  • When an interface is shutdown using ifconfig, an ioctl is issued which clears the IFF_UP bit and invokes stop.
  • The open method should acquired all required resources like IRQs and IO regions and release them in the stop method.
  • Hardware address processing sequence
    • At module init, obtain the hardware address from the device and store it in dev→dev_addr.
    • Before the user opens the device he can set the hw address using ifconfig, during which dev→dev_addr is modified.
    • When the device is opened the addr is copied from dev→dev_addr to the device.
    • The hardware address cannot be modified when the interface is up/open.
  • There is a queue between the driver and the networking subsystem. The open method should enable transmission on the queue by calling
void netif_start_queue(struct net_device *dev);
  • The queue should be disabled in the stop method using
void netif_stop_queue(struct net_device *dev);
 
driver-notes.txt · Last modified: 2005/08/16 00:03 by 220.225.128.84
 
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki
This work is licensed under the GNU Free Documentation License..