Linux device drivers
Linux device drivers exist as kernel modules for Linux in Linux. Kernel modules can dynamically scale the system while the system is running. So, we can dynamically install or unload modules in user space, using Insmod and Rmmod.
There are a lot of devices in the real world, which vary in electrical characteristics and I/O methods. To simplify the work of device-driven programmers, Linux systems extract common features from these disparate devices, dividing them into three main categories: Character devices, block devices, and network devices.
Linux Device Driver Model
The Linux device driver model extracts the common properties of device operations, abstracts them, and implements them in the kernel to provide a general unified interface for new additions or drivers, which makes the driver development easier, and the programmer just needs to learn the interface.
user Interface
With the complete hierarchical diagram of all the devices in the system, it is easy to export a complete view to the user space.
This is achieved through a special virtual file system called SYSFS, which allows the user to mount the file system at any node in the user space. Everything is under the so-called Linux file. SYSFS the variables and functions as interfaces to the user space, making the device driver become quite transparent, the device-driven operation can also be easily changed into a file operation. Combined with daemons and kernel event layers, SYSFS is the perfect channel for user space and kernel space communication.
Linux device underlying model
The device model is the hierarchy structure, each node of the level is realized through Kobject, and the file is embodied in the Sysfs file system.
struct KOBJECT data structures exist in the kobject structure kernel, and each kobject loaded into the system uniquely corresponds to a folder in the/sys or subdirectory. As you can say, many kobject structures form the hierarchical structure of the device model. Each kobject corresponds to one or more struct attribute that describes the structure of the property.
structKobject {Const Char*name;/ * Directory name corresponding to SYSFS * / structList_head entry;/ * KOBJETCT doubly linked list * / structKobject *parent;/ * point to Kobject in Kset, equivalent to the parent directory * / structKset *kset;/ * Point to the owning Kset * / structKobj_type *ktype;/ * Responsible for KOBJECT structure tracking * / structSysfs_dirent *SD;structKref Kref;/*kobject Reference count * / unsigned intState_initialized:1;unsigned intSTATE_IN_SYSFS:1;unsigned intState_add_uevent_sent:1;unsigned intState_remove_uevent_sent:1;unsigned intUevent_suppress:1;};
NIC Driver
Network equipment (also known as "network Card"), used to complete high-level network protocols (such as TCP/UDP, etc.) of the underlying data transmission and device control functions.
Sending a packet
Network devices do not have an entry point in the/dev directory, in other words, the network device does not exist as a device file in the system, and in the application layer, the user uses the network device through the socket function of the socket API.
The prototype functions of the socket API are:
<sys/socket.h>intsocket(intintint protocol)
Where family represents the protocol family used by the socket interface, including af_inet (IPV4 protocol family) and Af_inet6 (IPV6 protocol family).
The parameter type is used to denote the type of socket interface, with SOCK_STREAM (Byte throttle Socket Interface), SOCK_DGRAM (Datagram Socket interface), and SOCK_RAW (raw socket Interface).
Generally speaking, the third parameter of the function is usually set to 0 in actual use, unless it is used on the original socket interface.
The high-level network code in the Linux kernel then allocates SKB to store the network packets and then uploads the SKB to the driver's send function. The driver's send function typically uses DMA to transfer the SKB in main memory to the device, which is then sent by the hardware logic. After the hardware has successfully sent a data frame, it usually notifies the processor in an interrupted manner, and the interrupt handler for the corresponding driver is then called to handle the situation. For interrupts resulting from a successful send packet, the SKB in which the grouping is located is freed in its interrupt handler function.
Accept Packets
In addition to responding to requests from the kernel, the network device needs to process packets from the outside world asynchronously, and for a block device, it only needs to respond to requests from the kernel. This makes the design pattern of the network device driver, in addition to processing data wait, also need to complete such as address settings, configure network transport parameters and traffic statistics and other management classes of tasks.
The underlying network device notifies the driver in an interrupted manner after successfully transferring a packet to main memory. The latter is responsible for assigning a new SKB to accommodate the newly-entered grouping and then notifying the network subsystem of new packets arriving by calling the NETIF_RX function.
Location of network devices and drivers in the TCP/IP protocol stack
Network devices and drivers are the bottom of the physical layer and the data link layer, which is facing the physical media that actually undertakes the data Transmission task, provides specification and definition for the media of data communication, and mainly concerns the problem of transmitting bit stream on the communication line.
Structure of the network driver
All Linux network drivers follow a common interface and are designed with an object-oriented approach.
A device is an object (device structure) that has its own data and methods inside it.
Each device's method is called when the first parameter is the device object itself. This allows the method to access its own data (similar to the this reference when object-oriented programming).
The most basic method of a network device is initialization, sending, and receiving.
Basic methods of network drivers
Initialize (Initialize)
The driver must have an initialization method that invokes the initialization program when the driver is loaded into the system, and it does the following things:
Testing Equipment . In the initialization program you can check whether the hardware exists based on the characteristics of the hardware, and then decide whether to start the driver.
Configure and initialize the hardware . In the initialization program you can complete the configuration of the hardware resources, such as plug-and-play hardware can be configured at this time (the Linux kernel does not have good support for PnP function, can be done in the driver).
After you have configured or negotiated the resources that your hardware occupies, you can request these resources from the system. Some resources can be shared with other devices, such as interrupts. Some are not shared, such as IO, DMA.
Next you will initialize the variables in the device structure .
Finally, you can get the hardware to formally start working .
Turn on (open)
The Open method is called in the network device driver when the network device is activated (that is, the state of the device is Down–>up).
So in fact a lot of the work in initialize can be put here to do. such as the application of resources, the activation of hardware . If Dev->open returns non-0 (error), the state of the hardware is still down.
Another effect of the open method is that if the driver is mounted as a module, the device is turned on when the module is unloaded. In the open method, you call the Mod_inc_use_count macro.
Off (stop)
The Close method does the opposite of open work. Some resources can be freed to reduce the burden on the system . Close is called when the device state is changed from up to down.
In addition, if the driver is loaded as a module, close should call Mod_dec_use_count to reduce the number of times the device is referenced so that the driver can be uninstalled.
Additionally the Close method must return success (0==success).
-
Send (hard_start_xmit)
All network device drivers must have this send method. when the system calls the driver's xmit, the data sent is placed in a sk_buff structure. The
Generic driver sends data to the hardware. There are also special devices such as loopback that make data into a receiving data and send it back to the system, or the dummy device discards the data directly.
If the send succeeds, Sk_buff is released in the Hard_start_xmit method and returns 0 (sent successfully).
Returns 1 if the device is temporarily unable to process, such as hardware busy. At this point if the dev->tbusy is not 0, then the system thinks the hardware is busy, wait until Dev->tbusy 0 will not be sent again. The Tbusy 0 task is usually completed by an interrupt. The hardware will interrupt after sending, then the tbusy can be placed 0, and then the notification system can be sent again with MARK_BH (). In the case of unsuccessful delivery, you can also pail dev->tbusy to non-0, so the system will continue to try to resend. If the Hard_start_xmit send is unsuccessful, do not release sk_buff. The data in the transmitted Sk_buff already contains the frame headers required by the hardware. So in the sending method does not need to fill the hardware frame head, the data can be directly submitted to the hardware sent. The
Sk_buff is locked (locked), ensuring that other programs do not access it.
Receive (Reception)
The driver does not exist with a receive method. There is a data receipt that should be the driver to notify the system.
The general device generates an interrupt after receiving the data, and in the interrupt handler the driver requests a piece of Sk_buff (SKB), which is placed from the hardware readout data into the requested buffer.
Next, fill in some of the information in Sk_buff. Skb->dev = Dev, determines the type of protocol received for the frame, and fills in the Skb->protocol (Multi-protocol support).
Point the pointer skb->mac.raw to the hardware data and discard the hardware frame header (Skb_pull). Also set the Skb->pkt_type to indicate the second layer (link layer) data type. Can be of the following types:
Packet_broadcast: Link Layer Broadcast
Packet_multicast: Link Layer Multicast
Packet_self: Send yourself a Frame
Packet_otherhost: Frames sent to others (this is the case when listening mode)
the last Call to Netif_rx () transmits the data to the protocol layer . The data in the NETIF_RX () is placed in the processing queue and then returned, and the real deal is to reduce the outage time after the interrupt is returned.
After calling Netif_rx (), the driver will no longer be able to access the data buffer skb.
Address Resolution (XARP)
Some networks have hardware addresses (such as Ethernet) and need to know the destination hardware address when sending hardware frames. This requires the correspondence of the upper layer protocol address (IP, IPX) and the hardware address.
This correspondence is done through address resolution. The device that needs to do ARP calls the driver's Rebuild_header method before it is sent. The main parameters of the call include a pointer to the hardware frame header, and a protocol layer address.
If the driver resolves the hardware address, it returns 1, and if not, returns 0.
The call to Rebuild_header is in Net/core/dev.c's Do_dev_queue_xmit ().
Parameter Settings and statistics
In the driver also provides some methods for the system to set the parameters of the device and read information. Only Superuser (root) permissions are generally required to set device parameters.
The Setup methods are:
dev->set_mac_address () the MAC address of the device to be set when the user invokes the IOCTL type SIOCSIFHWADDR. General settings for Mac addresses do not make much sense.
dev->set_config () when the user invokes the IOCTL when the type is Siocsifmap, the system invokes the driver's Set_config method. The user passes a IFMAP structure that contains the required I/O, interrupts, and so on.
Dev->do_ioctl () The system calls this method of the driver if the user invokes the IOCTL between Siocdevprivate and siocdevprivate+15. Typically, you set up private data for your device. Reading information is also done through the IOCTL call.
The driver can also provide a dev->get_stats method that returns a Enet_statistics structure that contains the statistics to send and receive, in addition to the Times. The treatment of the IOCTL is in NET/CORE/DEV.C Dev_ioctl () and DEV_IFSIOC ().
Network device data structure
structdevice{/* * This is the first field of the ' visible ' part of this structure * (i.e. as seen by users in the "space.c" file) . It is the name * the interface. */ Char*name;/* I/O specific fields-fixme:merge these and struct ifmap into one * * unsigned LongRmem_end;/ * shmem "recv" End * / unsigned LongRmem_end;/ * shmem "recv" End * / unsigned LongRmem_start;/ * shmem "recv" Start * / unsigned LongMem_end;/ * Shared mem end * / unsigned LongMem_start;/ * Shared mem start * / unsigned LongBASE_ADDR;/* Device I/O address */ unsigned CharIrq/ * Device IRQ number * / / * Low-level status flags. * * volatile unsigned CharStart/ * Start an operation * /Interrupt/ * Interrupt arrived * / /* Interrupt is set to 1 when processing interrupts and 0 is cleared. */ unsigned LongTbusy;/ * Transmitter busy must is long for struct device *next;/* the device initialization function. Called only once. */ / * The initialization method that points to the driver. */ int(*init) (structDevice *dev);/ * Some Hardware also needs these fields, but they is not part of the usual set specified in space.c. * / / * Some hardware can support multiple interfaces on a board, possibly using If_port. */ / * Some hardware can support multiple interfaces on a board, possibly using If_port. */ unsigned CharIf_port;/ * selectable AUI, TP,.. */ unsigned CharDma/ * DMA Channel * / structEnet_statistics* (*get_stats) (structDevice *dev);/ * * This marks the end of the ' visible ' part of the structure. All * fields hereafter is internal to the system, and could change at * would (Read:may is cleaned up at would). */ /* These may is needed for the future network-power-down code. * / / * Trans_start records the last successful send time. Can be used to determine if the hardware is working properly. */ unsigned LongTrans_start;/* Time (in jiffies) of the last Tx */ unsigned LongLAST_RX;/* Time of last Rx * / / * flags contain a lot of content, defined in include/linux/if.h. */ unsigned ShortFlags/* Interface flags (a la BSD) */ unsigned ShortFamily/* Address family ID (af_inet) */ unsigned ShortMetric/* Routing metric (not used) */ unsigned ShortMtu/ * Interface MTU value * / / * Type indicates the types of physical hardware. The main explanation is whether the hardware requires ARP. defined in the Include/linux/if_arp.h. */ unsigned ShortType/ * Interface Hardware type * / / * The upper protocol layer reserves the hardware frame header space in front of the send data buffer based on Hard_header_len. */ unsigned ShortHard_header_len;/ * Hardware HDR length * / / * Priv points to some parameters defined by the driver itself. */ void*priv;/ * Pointer to private data * / / * Interface Address info. * * unsigned CharBroadcast[max_addr_len];/ * HW bcast Add * / unsigned CharPad/ * make dev_addr aligned to 8bytes * / unsigned CharDev_addr[max_addr_len];/ * HW Address * / unsigned CharAddr_len;/ * Hardware Address length * / unsigned LongPA_ADDR;/ * Protocol address * / unsigned LongPA_BRDADDR;/ * Protocol Broadcast addr * / unsigned LongPA_DSTADDR;/ * Protocol P-Side addr * / unsigned LongPa_mask;/ * Protocol netmask * / structDev_mc_list *mc_list;/ * Multicast MAC addresses * / intMc_count;/* Number of installed mcasts * / structIp_mc_list *ip_mc_list;/ * IP Multicast filter chain * /__u32 Tx_queue_len;/ * Max frames per queue allowed * / / * For load Balancing driver pair support * / unsigned LongPkt_queue;/ * Packets queued * / structDevice *slave;/ * Slave device * / structNet_alias_info *alias_info;/ * Main Dev alias info * / structNet_alias *my_alias;/ * Alias devs * / / * Pointer to the interface buffers. * / structSk_buff_head Buffs[dev_numbuffs];/ * Pointers to interface service routines. * / int(*open) (structDevice *dev);int(*hard_start_xmit) (structSk_buff *SKB,structDevice *dev);int(*hard_header) (structSk_buff *SKB,structDevice *dev,unsigned ShortTypevoid*DADDR,void*SADDR,unsignedLen);int(*rebuild_header) (void*eth,structDevice *dev,unsigned LongRADDR,structSk_buff *SKB);#define Have_multicast void(*set_multicast_list) (structDevice *dev);#define HAVE_SET_MAC_ADDR int(*set_mac_address) (structDevice *dev,void*ADDR);#define HAVE_PRIVATE_IOCTL int(*DO_IOCTL) (structDevice *dev,structIfreq *ifr,intCMD);#define Have_set_config int(*set_config) (structDevice *dev,structIfmap *Map);#define Have_header_cache void(*header_cache_bind) (structHh_cache **HHP,structDevice*dev,unsigned ShortHtype, __u32 daddr); *dev,unsigned ShortHtype, __u32 daddr);void(*header_cache_update) (structHh_cache *hh,structDevice*dev,unsigned Char* haddr);#define HAVE_CHANGE_MTU structIw_statistics* (*get_wireless_stats) (structDevice *dev);};
Linux Network Driver