2.1Network Driver Structure
The architecture 1 of the Linux Network Driver is shown in.
It can be divided into four layers:(1) Protocol Interface Layer
(2) network device interface layer
(3) Device Driver Function Layer
(4) network device and media layer.
Network Driver Focus: completes the device driver Function Layer
In Linux, all network devices are abstracted as an interface ). This interface provides a set of operations for all network devices, represented by the data structure struct net_device.
The data structure net_device contains many device methods for system access and protocol layer calls, including the init function for device initialization and registration to the system, the open and stop functions for enabling and disabling network devices, the hard _ start_xmit function for processing data packets, and the interrupt processing function. For details about the net_device data structure (net_device in the kernel), see/Linux/include/Linux/netdevice. h.
2.2
Network-driven implementation mode
Linux network device drivers can be implemented in two ways:
(1) When the kernel is started, the network device driver is loaded. After the kernel is started, the Network Driver function is implemented immediately;
(2) modules are loaded.
Compared the two, the second form is more flexible. The module loading mode is discussed here.
(1) insmod is used to insert the network device driver into the kernel.
(2) insmod will call the init_module () function
(3) assign values to the dev-> init function pointer,
(4) The register_netdev () function registers the network device. If the network device is initialized successfully,
Insert the net_device data structure to the end of the dev_base linked list.
(5) The specific implementation process of the module to uninstall rmmod is shown in figure 2.
2.3Basic Methods for network drivers
2.3.1
Initialization
Net_device-> init refers to the function to complete.
(1) check whether the device exists
(2) Resource allocation
(3) construct net_device and initialize the net_device variable with the detected data value.
(4) register the device with the Linux kernel and apply for memory space.
2.3.2 open)
Open: called when the network device is activated (that is, the device status is down --> up ). So in fact, a lot of work in initialize can be done here. Such as resource application and hardware activation. If Dev-> open returns non-0 (error), the hardware status is down.
Another function of open is to prevent the device from being turned on when the driver is loaded as a module. Call the mod_inc_use_count macro in the open method.
2.3.3 stop)
Close to release the resource, which is called when the device status changes from up to down. As a module-mounted driver, close should call mod_dec_use_count to reduce the number of times the device is referenced so that the driver can be uninstalled. The close method must return success (0 = success ).
2.3.4 sending of data packets
(1) network device initialization function (net_device-> init)
(2) open a network device (net_device-> open)
(3) Use net_device-> hard_header to create a hardware Baotou function pointer
(4) Finally, net_device-> hard_start_xmit is called through the Protocol interface layer function dev_queue_xmit (for details, see/Linux/NET/CORE/dev. c) to send data packets.
(5) If the sending is successful, sk_buff will be released in the hard_start_xmit method, and 0 will be returned (the sending is successful ).
If it cannot be processed temporarily, for example, if the hardware is busy, 1 is returned. In this case, if Dev-> tbusy is set to non-0, the system considers the hardware to be busy and will not send it again until Dev-> tbusy is set to 0.
The zero-point task of tbusy is generally completed by the interruption. The hardware is interrupted after sending. In this case, you can set tbusy to 0, and then use mark_bh () to call the notification system to send it again.
2.3.5Receive) The driver does not have a receiving method. If data is received, the driver notifies the system.
(1) An interruption occurs when the device receives the data.
(2) The driver in the interrupt processing program applies for a piece of sk_buff (SKB) and reads data from the hardware and places it in the requested buffer zone.
(3) fill in the relevant information in sk_buff. SKB-> Dev = Dev, determine the protocol type of the received frame, and enter SKB-> protocol (supported by multiple protocols ). Point the pointer SKB-> Mac. Raw to hardware data and then discard the hardware frame header (skb_pull ). You must also set SKB-> pkt_type to indicate the second (Link Layer) data type. It can be of the following types:
Packet_broadcast: Link Layer Broadcast
Packet_multicast: Layer Multicast
Packet_self: The frame sent to you.
Packet_otherhost: The frame sent to someone else (this frame will be available in the listening mode)
(4) Call netif_rx () to transmit the data to the protocol layer.
2.3.6 hardware frame header (hard_header)
Hardware adds its own hardware frame header before sending upper-layer data (for example, Ethernet has a 14-byte frame header)
Related Function hard_header
2.3.7 Address Resolution (xarp)
Some networks have hardware addresses (such as Ethernet) and need to know the destination hardware address when sending hardware frames. In this way, the upper-Layer Protocol address (IP, IPX) and hardware address are required. This corresponds to the address resolution. The device that requires ARP will call the rebuild_header method of the driver before sending it. The main parameters of the call include the pointer to the hardware frame header and the protocol layer address. If the driver can parse the hardware address, 1 is returned. If not, 0 is returned.
The rebuild_header is called in do_dev_queue_xmit () of net/CORE/dev. C.
2.3.8 parameter settings and statistics
The driver also provides methods for the system to set and read device parameters. Generally, only the root user can set the device parameters. The settings are as follows:
Dev-> set_mac_address ()
When the ioctl type is siocsifhwaddr, you need to set the MAC address of the device. Generally, it does not make much sense to set the MAC address.
Dev-> set_config ()
When the type of IOCTL is siocsifmap, the system calls the set_config method of the driver. The user will pass an ifmap structure containing the required I/O, interrupt and other parameters.
Dev-> do_ioctl ()
Dev-> do_ioctl ()
If the type of IOCTL is between siocdevprivate and siocdevprivate + 15, the system will call this method of the driver. Generally, it is used to set the dedicated data of the device.
The read information is also called by IOCTL. The driver can also provide
Dev-> get_stats method, returns an enet_statistics structure, containing the statistics of sending and receiving.
IOCTL is processed in dev_ioctl () and dev_ifsioc () of net/CORE/dev. C.
2.4 data structures used in network drivers
Net_device is the core part of the network driver. The most important part is the data structure of the network device. It is defined in include/Linux/netdevice. h. Its annotations are sufficiently detailed.
The devie Methods
INT (* open) (struct net_device * Dev );
INT (* Stop) (struct net_device * Dev );
INT (* hard_start_xmit) (struct sk_buff * SKB, struct net_device * Dev );
Initialize the transmission of data packets.
2.5 common system support
2.5.1 memory application and release
Include/Linux/kernel. h declares kmalloc () and kfree (). It is used to apply for and release memory in kernel mode.
Void * kmalloc (unsigned int Len, int priority );
Void kfree (void * _ PTR );
Unlike malloc () in user mode, the size of the kmalloc () application space is limited. The length is the integer power of 2. The maximum length that can be applied is limited. In addition, kmalloc () has the priority parameter, which can usually be gfp_kernel in use. If the gfp_atomic parameter is called in the interrupt, the caller may enter the sleep state because gfp_kernel is used, it is not allowed to handle interruptions.
The memory released by kfree () must be applied by kmalloc. If you know the memory size, you can also use kfree_s () to release it.
2.5.2 request_irq (), free_irq ()
This is the call for the driver to request and release the interrupt. Declare it in include/Linux/sched. h. Request_irq () call definition:
Int request_irq (unsigned int IRQ,
Irqreturn_t (* Handler )(),
Unsigned long flags,
Const char * dev_name,
Void * dev_id );
IRQ: The hardware interrupt number to be applied. On Intel Platform, the range is 0--15.
Handler: the interrupt handler function registered with the system.
Irqflags: the attribute of Interrupt Processing.
(1) sa_interrupt, indicating a fast interruption. All interruptions are blocked during the call.
(2) The sa_shirq attribute sets the sharing interruption of multiple devices in the future.
(3) The sa_sample_random interrupt Timestamp can generate system entropy.
Dev_id: Used to interrupt sharing. It is generally set to the device structure of the device itself or null. The interrupt handler can use dev_id to find the device that controls the interrupt, or use rq2dev_map to find the device.
Void free_irq (unsigned int IRQ, void * dev_id );
2.5.3 clock
The clock processing is similar to an interrupt. It is also a time processing function registered. The system will call this function after the specified time. Declare it in include/Linux/Timer. h.
Struct timer_list {
Struct list_head list;
Unsigned long expires;
Unsigned long data;
Void (* function) (unsigned long );
};
Void add_timer (struct timer_list * timer );
Int del_timer (struct timer_list * timer );
Void init_timer (struct timer_list * timer );
(1) declare a timer_list structure and call init_timer to initialize it.
(2) In the time_list structure, expires indicates the cycle of the clock, in the unit of jiffies (1/Hz ).
(3) A function is a callback function after the time reaches. Its parameter is data in timer_list.
(4) The data parameter is assigned a value during clock initialization and is generally assigned to the device structure pointer of the device.
During the preset time, the system calls the function and clears the time_list from the scheduled queue. So if you need to keep using the timer function, you need to call add_timer () again in the function to call this function.
2.5.4 access and use of I/O ports:
Inline unsigned int INB (unsigned short port );
Inline unsigned int inb_p (unsigned short port );
Inline void outb (char value, unsigned short port );
Inline void outb_p (char value, unsigned short port );
Defined in include/ADM/IO. h.
Inb_p (), outb_p (), INB (), and outb_p () are different in that the former has a waiting time when accessing I/O.
(Pause) a low-speed I/O device
Prevent Access to I/O conflicts (1) before using the port, check whether the required I/O is in use. If not, mark the port as in use, release after use.
Int check_region (unsigned int from, unsigned int extent );
Void request_region (unsigned int from,
Unsigned int extent,
Const char * Name );
Void release_region (unsigned int from, unsigned int extent );
The from parameter indicates the starting address of the I/O port used, and the extent indicates the number of ports starting from. Name indicates the device name.
2.5.5 enable or disable an interrupt
The system provides the driver with the ability to open and close the response to interruptions. Is two definitions in include/ASM/system. h.
# Define CLI () _ ASM _ volatile _ ("CLI "::)
# Define STI () _ ASM _ volatile _ ("Sti "::)
2.5.6 print information
Similar to printf () in a common program, the driver needs to output information using printk (). Declare it in include/Linux/kernel. h.
Int printk (const char * FMT ,...);
FMT is a formatted string .... Yes. Both are in the same format as printf.
2.5.7 register the driver
If you use the module method to load the driver, you need to register the device to the system device table during module initialization. Remove the device from the system when it is no longer in use. Define two functions in drivers/NET/net_init.h to complete this job.
Int register_netdev (struct net_device * Dev );
Void unregister_netdev (struct net_device * Dev );
Dev: The device structure pointer to be registered. Note: the most important thing about net_device is the name pointer and init method. If the name pointer is null or the content is ''or name [0] is space, the system treats the device as an Ethernet device.
Register_netdev () returns 0, indicating that the operation is successful. Otherwise, the operation fails.
2.5.8 sk_buff
Data transmission between different layers of the Linux network is performed through sk_buff, which is the key to the efficient operation of the Linux system network. Sk_buff includes the control method and data buffer. There are two types of control methods by function: (1) one is to control the entire buffer chain (2) the method to control the data buffer.
Sk_buff is organized into a two-way linked list. According to the characteristics of network applications, the operation on the linked list is mainly to delete the elements of the linked list header and add them to the end of the linked list.
. Alloc_skb () applies for a sk_buff and initializes it. The requested sk_buff is returned.
. Dev_alloc_skb () is similar to alloc_skb. After the buffer zone is applied, the 16-byte frame header space is retained. It is mainly used in Ethernet drivers.
. Kfree_skb () releases a sk_buff.
. Skb_clone () copies a sk_buff, but does not copy the data.
. Skb_copy () completely copies a sk_buff.
. Skb_dequeue () extracts the first element from a sk_buff linked list. The retrieved sk_buff is returned. If the linked list is empty, null is returned. This is a common operation.
. Skb_queue_head () puts an element in the header of A sk_buff linked list.
. Skb_queue_tail () puts an element at the end of a sk_buff linked list. This is also a common operation. Network data processing is mainly used to manage a first-in-first-out queue. skb_queue_tail () and skb_dequeue () are used to complete this task.
. Skb_insert () inserts an element before an element in the linked list.
. Skb_append () inserts an element after an element in the linked list. For some protocols (such as TCP), skb_insert () and skb_append () are used to reorganize data that do not arrive in sequence ().
. Skb_reserve () reserves a space in the buffer zone of the applied sk_buff. This space is generally used as the header space of the next layer of protocol.
. Skb_put () reserves a space for data in the buffer zone of the applied sk_buff. In
After alloc_skb, the buffer of the applied sk_buff is in the Free State. A tail Pointer Points to the free space. In fact, tail points to the buffer header at the beginning. Skb_reserve () applies for the protocol header space in the free space, and skb_put () applies for the data space. See the figure below.
. Skb_push () moves the data space in the sk_buff buffer forward. That is, the space in the head room is moved to the data area.
. Skb_pull () moves the space in the data area in the sk_buff buffer to the head room.
Previous Article: [Linux Device Driver] device drivers (I)
Next article: [Linux Device Driver] network device driver considerations (3)