Linux network card driver writing
[Excerpted from LinuxAid]
Work needs to write Linux drivers for our company's network card. Experience an important part of an insensitive process and deep sense of technical exchange. As a strong weapon challenged Microsoft monopoly, Linux is increasingly loved by everyone. I really hope she can grow rapidly in China. Put the program document, I hope to discuss Linux technology and applications with you to promote Linux in China.
Linux operating system network driver writing
One. LINUX system device driver overview
1.1 Linux device driver classification
1.2 Some basic concepts written by the driver
Two .linux system network device driver
2.1 The structure of the network driver
2.2 Basic methods of network drivers
2.3 Data structure used in network drivers
2.4 Common system support
3. Writing issues that may encounter in Linux network drivers
3.1 Interrupt Sharing
3.2 Hardware to send a busy time
3.3 Flow Control (Flow Control)
3.4 debugging
Fourth. Further reading
V. Miscellaneous
One. LINUX system device driver overview
1.1 Linux device driver classification
The Linux device driver has a large proportion of Linux's kernel source code, and the length of the source code is increasing, mainly the increase in the driver. During the continuous upgrade of the Linux kernel, the structure of the driver is relatively stable. In fluctuation of 2.0.xx to 2.2.xx, the driver's writing has changed, but the transplant from 2.0.xx to 2.2.xx is only a small amount of work.
The device of the Linux system is divided into three kinds of Char Device, block devices, and network devices. The character device is a device that does not have a cached when the access is accessed. The reading and writing of the block device has a cache, and the block device must be able to access (Random Access), and the character device does not have this request. Typical character devices include mice, keyboards, serial ports, and the like. Block equipment mainly includes a hard disk floppy disk device, a CD-ROM, and the like. A file system must install access the operating system must be on the block device.
Network equipment do special processing in Linux. Linux's network system is primarily based on the Socket mechanism for BSD UNIX. Defining a special data structure (SK_BUFF) between the system and drivers. The system supports the cache of sending data and receiving data, providing traffic control mechanisms, providing support for multi-protocols.
1.2 Some basic concepts written by the driver
No matter what the operating system driver, there are some universal concepts. The operating system is provided to the driver is also roughly the same. The following briefly introduces some basic requirements for the network device driver.
1.2.1 Send and receive
This is the most basic function of network devices. The network card is nothing more than the transmission and reception work. So the driver is to tell the system where your send function is, the system will call your sender when there is data to be sent. There is also a driver because it is directly manipulating hardware, so network hardware has data to receive the first possible to get this data is the driver, which is responsible for performing these raw data and then gives the system. Here, the operating system must provide two mechanisms, one is to find the send function of the driver, one is the driver to give the received data to the system.
1.2.2 interrupt
Interrupts have an important role in the modern computer structure. The operating system must provide the capabilities of the driver response interrupt. Generally, an interrupt handler is registered in the system. The operating system calls the handler of the driver after the hardware interrupt occurs. Linux supports the interrupt sharing, that is, multiple devices share an interrupt.
1.2.3 clock
Many places will be used in many places when implementing the driver. For timeout processing in some protocols, there is no polling of hardware for interrupting mechanisms. The operating system should provide a timed mechanism for the driver. Generally, the clock function is registered after a predetermined time. In the network driver, if the hardware has no interrupt, the timer can provide the polling (POLL) to access the hardware. Or the timeout retransmission required to achieve some protocols.
Two .linux system network device driver
2.1 The structure of the network driver
All Linux network drivers follow the universal interface. The object-oriented method is used. A device is an object (DEVICE structure), which has its own data and methods inside. The first parameter of each device is called by this device object itself. This method can access its own data (THIS reference when designing the object program).
A network device is the most basic method has initialization, sending, and receiving.
---------------------------------------
| DELIVER PACKETS | | Receive Packets Queue |
| (dev_queue_xmit ()) | | Them (Netif_Rx ()) |
---------------------------------------
| | | /
/ | | |
-------------------------------------------------- -----
| Methods and Variables (Initialize, Open, Close, Hard_Xmit, |
Interrupt Handler, Config, Resources, Status ...) |
-------------------------------------------------- -----
| | | /
/ | | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| send to hardware | | receivce from hardware |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| | | /
/ | | |
-------------------------------------------------- ---
Hardware Media |
-------------------------------------------------- ---
The initialization program completes the initialization of the hardware, the initialization of variables in Device, and the application for system resources. The sender is automatically called when the driver's upper protocol layer has data to be sent. The transmitted data is not caught in the general driver, but the data is transmitted directly using the hardware transmission function. Receiving data is typically notified by hardware interrupts. In the interrupt handler, fill the hardware frame information in a SKBUFF structure, and then call Netif_RX () to the upper process.
2.2 Basic methods of network drivers
The network device is an object that provides some methods for system access. It is these ways to have a unified interface that masks the specific details of the hardware, allowing the system to use a unified form of access to various network devices to do hardware unrelated.
The most basic method is explained below.
2.2.1 Initialize
The driver must have an initialization method. This initializer is called when loading the driver into the system. It does the following work. Testing Equipment. In the initialization program you can check if the hardware exists according to the feature of the hardware, and then determine if this driver is started. Configure and initialize hardware. In the initialization program you can complete the configuration of the hardware resource, such as the plug-and-play hardware can be configured at this time (the Linux kernel is not well supported by the PNP function, you can do this in the driver). After configuring or negotiates the resource occupied by hardware, you can apply for these resources to your system. Some resources are can be shared with other devices, such as interrupts. Some are unable to share, such as IO, DMA. Next you want to initialize the variables in the DEVICE structure. Finally, you can let the hardware officially start working.
2.2.2 Open (Open)
This method is called when the network device driver is activated (ie the device status is Down -> Up). So actually a lot of work in Initialize can be made here. For example, the application of resources, the activation of hardware. If DEV-> Open returns non-0 (error), the status of the hardware is still Down.
The OPEN method is another role that if the driver is loaded as a module, the device is to be opened when the module is uninstalled. In the OPEN method, you want to call the MOD_INC_USE_COUNT macro.
2.2.3 Close (STOP)
The Close method does the opposite of Open. Certain resources can be released to reduce system burden. Close is called when the device status is converted by the UP to DOWN. In addition, if it is a driver loaded as a module, MOD_DEC_USE_COUNT should be called in the close to reduce the number of times the device is referenced, so that the driver can be unloaded.
In addition, the Close method must return success (0 == Success).
2.2.4 Send (HARD_START_XMIT)
All network device drivers must have this sending method. When the XMIT of the system calls the driver, the transmitted data is placed in a SK_BUFF structure. The general driver transmits the data to the hardware. There are also some special devices such as loopback to make data into a received data and then return to the system, or Dummy devices directly discard the data.
If the send is successful, SK_BUFF is released in the hard_start_xmit method, and 0 (successful transmission). If the device is temporarily unable to handle, such as hardware is busy, return 1. At this time, if the dev-> TBUSY is set to be non-0, the system considers that the hardware is busy, and it will be sent again after DEV-> TBUSY set. Tbusy 0 tasks are generally completed by interrupts. The hardware generates an interrupt after the transmission ends, then TBUSY can be set to the notification system with the Mark_BH () calling system can be sent again. In the case where the transmission is unsuccessful, it is also possible to do not set up dev-> TBUSY as non-0, so the system will continue to resend. If the hard_start_xmit is unsuccessful, do not release SK_BUFF. The data in the transmitted SK_BUFF already contains the frame header required for hardware. Therefore, no need to fill the hardware frame header in the transmission method, and the data can be directly submitted to the hardware. SK_BUFF is locked (Locked), make sure other programs do not access it.
2.2.5 Reception
There is no receiving method without the driver. There is a data received to be a driver to notify the system. The general device will generate an interrupt after receiving the data, and the driver is applied in the interrupt handler to apply for a SK_BUFF (SKB), and place it from the hardware read data to the buffer of the application. Next, populate some information in SK_Buff. SKB-> dev = dev = dev = dev, judge the protocol type received by the frame, fill in SKB-> Protocol (support for multi-protocol). Point the pointer SKB-> Mac.RAW to the hardware data and discard the hardware frame head (SKB_PULL). Also set SKB-> PKT_TYPE, indicate the second layer (link layer) data type. Can be the following type:
Packet_Broadcast: Link Mass Broadcast
Packet_multicast: Link Layer Multicast
Packet_self: Frame sent to yourself
Packet_otherhost: Send it to the frame of others (this frame will have this frame when listening mode)
Finally, Netif_Rx () is called to the protocol layer. Netif_rx () in the data is placed in the processing queue and then returns, the real process is to reduce the interrupt time after the interrupt is turned back. After calling Netif_Rx (),
The driver cannot access the data buffer SKB.
2.2.6 Hardware Frame Head (HARD_HEADER)
Hardware generally adds its own hardware frame head before the upper layer data is sent, such as the Ethernet (Ethernet) has 14-byte frame headers. This frame header is to add in front of the upper layer IP, IPX and other packets. The driver provides a Hard_Header method, a protocol layer (IP, IPX, ARP, etc.) call this program before sending data.
The length of the hardware frame head must be filled in dev-> hard_header_len, so that the protocol layer retains the space of the hardware frame before returning to the data. This way, the hard_header program can be called SKB_PUSH and then fill in the hardware frame. When the protocol layer calls hard_header, the transferred parameters include (2.0.xx): SK_BUFF, DEVICE pointer, protocol, destination address (DADDR), source address (SADDR), Data Length (LEN). The data length does not use the parameters in SK_BUFF because the data may not fully organize it when calling hard_header. Saddr is NULL if the default address is used. Daddr is NULL indicating that the protocol layer does not know the hardware destination address. If the hard_header completely fills the hardware frame header, return the number of bytes. If the information in the hardware frame head is not complete (such as DADDR is NULL, the hardware address is required in the frame header. Typically, the Ethernet requires address resolution (ARP), returns the number of negative bytes. With a negative number of Hard_Header, the protocol layer will make further build header's work in the BUILD HEADER. At present, the Linux system is ARP (if hard_header returns positive, dev-> arp = 1, indicating that there is no need to do ARP, return negative, dev-> arp = 0, do ARP).
The call to the Hard_Header in the handler of each protocol layer. Such as ip_output.
2.2.7 Address Analysis (XARP)
Some networks have hardware addresses (such as Ethernet) and need to know the hardware address of the destination when sending hardware frames. This requires the upper layer protocol address (IP, IPX), and the hardware address. This correspondence is done by address parsing. The device you need to do ARP will call the REBUILD_HEADER method for the driver before sending. The main parameters of the call include a pointer to the hardware frame header, the protocol layer address. If the driver is able to resolve the hardware address, return 1, if it is not possible, return 0.
The call to Rebuild_Header in DO_DEV_QUE_XMIT () of NET / CORE / dev.c.
2.2.8 Parameter settings and statistics
Some methods are also provided in the driver for setting and reading information on the parameters of the device. Generally only the superuser (root) permission can set the device parameters. The setting method is:
DEV-> set_mac_address ()
When the user calls the IOCTL type for SiocSifhwaddr, the MAC address of this device is set. Generally, the setting of the MAC address is not much significant.
DEV-> set_config ()
When the user calls IOCTL, the system will call the SET_CONFIG method of the driver. Users will pass an IFMap structure containing the required I / O, interrupt and other parameters.
DEV-> do_iocTl ()
This method of the driver is called between SiocDevPrivate and SiocDevate 15 if the user calls IOCTL. Generally, the dedicated data of the device is set.
Reading information is also made via IOCTL calls. Except for drivers can also provide one
DEV-> GET_STATS method, return a ENET_STATISTICS structure, including statistics that send reception. The processing of IOCTL is in dev_ioctl () and dev_ifsioc () in Net / Core / Dev.c.
Linuxman@263.net
.3 Data structure used in network drivers
The most important thing is the data structure of network devices. Define in include / Linux / NetDevice.h. Its annotations have been exhausted.
Struct Device
{
/ *
* This is the first field of the "visible" part of this structure
* (i.ec "file). It is the name
* The interface.
* /
Char * name;
/ * I / O Specific Fields - fixme: Merge these and struct ifmap INTO One * /
Unsigned long rmem_end; / * shMem "Recv" end * /
Unsigned long rmem_start; / * shMem "Recv" start * /
Unsigned long Mem_end; / * shared mem end * /
Unsigned long mem_start; / * shared mem st * /
Unsigned long base_addr; / * device I / o address * /
Unsigned char Irq; / * Device Irq number * /
/ * Low-level status flags. * /
Volatile Unsigned Char Start, / * Start An Operation * /
Interrupt; / * Interrupt Arrived * /
/ * Interrupt is set to 1 when the interrupt is handled, and the processing is complete. * /
Unsigned long tbusy; / * Transmitter Busy Must Be long for
Bitops * /
Struct Device * Next;
/ * The Device Initialization function. Called Only onCE. * /
/ * Point to the initialization method of the driver. * /
INT (* init) (Struct Device * dev);
/ * Some Hardware Also Needs There Fields, But They Are Not Part of The
USUAL SECIED IN SPACE.C. * /
/ * Some hardware can support multiple interfaces on a single board, which may be used to IF_Port. * /
Unsigned char if_port; / * selectable aui, tp, .. * /
Unsigned char DMA; / * DMA Channel * /
Struct eNet_statistics * (* get_stats) (Struct Device * dev);
/ *
* This Marks The end of the "visible" part of the structure. All
* Fields Hereafter area Internal to the system, and may change at
* Will (Read: May Be Clealed Up at Will).
* /
/ * Sse May Be Needed for Future Network-Power-Down Code. * /
/ * Trans_start Record the time of the last successful transmission. Can be used to determine if the hardware is working properly. * /
Unsigned long trans_start; / * Time (in jiffies) of last tx * /
Unsigned long last_rx; / * time of last rx * /
/ * There are a lot of content in flags, defined in include / linux / if.h. * /
UNSIGNED SHORT FLAGS; / * Interface Flags (A LA BSD) * /
Unsigned short family; / * address family id (AF_INET) * /
Unsigned short metric; / * routing metric (not used) * / unsigned short mtu; / * interface mtu value * /
/ * TYPE indicates the type of physical hardware. It mains shows if the hardware needs ARP. Definition
INCLUDE / Linux / if_arp.h. * /
Unsigned short type; / * interface hardware type * /
/ * The upper protocol layer reserves the hardware frame header space in front of the transmitted data buffer according to Hard_Header_len. * /
Unsigned short hard_header_len; / * hardware hdr length * /
/ * Priv points to some parameters defined by the driver. * /
Void * priv; / * Pointer to Private Data * /
/ * Interface address info. * /
Unsigned char Broadcast [MAX_ADDR_LEN]; / * HW BCAST ADD * /
Unsigned charpa; / * make dev_addr aligned to 8
BYTES * /
Unsigned char dev_addr [max_addr_len]; / * hw address * /
Unsigned char addr_len; / * hardware address length * /
Unsigned long pa_addr; / * protocol address * /
Unsigned long pa_brdaddr; / * protocol Broadcast Addr * /
Unsigned long pa_dstaddr; / * protocol p-p Other Side Addr * /
Unsigned long pa_mask; / * protocol netmask * /
UNSIGNED SHORT PA_ALEN; / * Protocol Address Length * /
Struct dev_mc_list * mc_list; / * Multicast Mac Addresses * /
INT MC_COUNT; / * NUMBER OF Installed Mcasts * /
STRUCT IP_MC_LIST * IP_MC_LIST; / * ip multicast filter chain * /
__U32 TX_QUE_LEN; / * MAX FRAMES Per Queue Allowed * /
/ * For load balancing driver pair support * /
Unsigned long pkt_queue; / * packets queued * /
Struct Device * Slave; / * Slave Device * /
Struct Net_Alias_info * alias_info; / * main dev alias info * /
Struct Net_Alias * my_alias; / * alias devs * /
/ * Pointer to the interface buffers. * /
Struct SK_Buff_Head Buffs [dev_numbuffs];
/ * Pointers to Interface Service Routines. * /
INT (* Open) (Struct Device * dev);
INT (* STOP) (STRUCT Device * DEV);
INT (* hard_start_xmit) (Struct SK_Buff * SKB,
Struct Device * dev);
INT (* hard_header) (Struct Sk_buff * SKB,
Struct Device * dev,
Unsigned Short Type,
Void * Daddr, Void * Saddr,
Unsigned len);
INT (* rebuild_header) (void * eth, struct device * dev,
Unsigned long raddr, struct SK_Buff * SKB);
#define have_multicast
void (* set_multicast_list); "Struct Device * dev);
#define Have_set_mac_addr
INT (* SET_MAC_ADDRESS (Struct Device * dev, void * addr);
#define have_private_ioctl
INT (* do_ioc, struct device * dev, struct ifReq * IFR, int CMD);
#define have_set_config
INT (* set_config) (Struct Device * dev, struct ifmap * map);
#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;
#define have_change_mtu
INT (* CHANGE_MTU) (Struct Device * dev, int new_mtu);
Struct IW_Statistics * (* get_wireless_stats) (Struct Device * dev);
}
2.4 Common system support
2.4.1 Memory application and release
INCLUDE / Linux / kernel.h declares Kmalloc () and KFree (). Used to apply and release memory in kernel mode.
Void * kmalloc (unsigned int Len, int priority);
Void Kfree (void * __ ptr);
Different from malloc () in user mode, the kmalloc () application space has a size limit. The length is 2, the whole party. The maximum length of the application can be applied. In addition, Kmalloc () has a Priority parameter, which is usually used for GFP_kernel, if you call with GFP_ATOMic parameters in the interrupt, because using GFP_kernel, the caller may enter the SLEEP state, which is not allowed when the interrupt is processed.
The memory released by KFree () must be a kmalloc () application. If you know the size of the memory, you can also release it with kfree_s ().
2.4.2 Request_irq (), Free_IRQ ()
This is the call to the driver application interrupt and release interrupt. Declare in include / linux / sched.h.
Request_irq () Call definition:
INT Request_irq (unsigned int IRQ,
Void (* Handler) (INT IRQ, Void * dev_id, struct pt_regs * regs),
Unsigned long Irqflags,
Const char * devname,
Void * dev_id);
IRQ is the hardware interrupt number to apply. In the Intel platform, ranges 0--15. Handler is an interrupt handler that registers the system. This is a callback function. When interrupt occurs, the system calls this function, and the incoming parameter includes the hardware interrupt number, Device ID, register value. DEV_ID is the parameter dev_id of the system when the following request_irq. Irqflags is some properties of interrupt processing. More important than SA_Interrupt, indicate that the interrupt handler is a quick handler (settings sa_interrupt) or slow handler (not set sa_interrupt). Shield all interrupts when the quick handler is called. The slow handler is not masked. There is also a SA_SHIRQ property, which is set up after running multiple devices shared interrupts. DEV_ID will be used when interrupts. Generally set to the Device structure itself or NULL of this device. The interrupt handler can find the corresponding device that controls this interrupt by DEV_ID, or finds an interrupt corresponding to the interrupt with IRQ2DEV_MAP.
Void free_irq (unsigned int IRQ, VOID * DEV_ID);
2.4.3 clock
The processing of clock is similar to interrupt, but also registered a time handler, after a predetermined time, the system will call this function. Declare in include / linux / timer.h.
Struct Timer_List {
Struct Timer_list * next;
Struct Timer_List * prev;
Unsigned long expires;
UNSIGNED Long Data;
VOID (* Function) (UNSIGNED Long);
}
Void add_timer (struct timer_list * time);
INT DEL_TIMER (Struct Timer_List * Timer);
Void init_timer;
Use the clock, first declare a Timer_List structure, call the init_timer to initialize it.
ExpiRes in the Time_List structure is the cycle of this clock, and units use Jiffies units.
Jiffies is a global variable for Linux, representing time. Its unit is different from the hardware platform.
A constant Hz is defined in the system that represents the number of minimum time intervals per second. In this way, Jiffies is 1 / Hz. The unit of Intel platform Jiffies is 1/100 seconds, which is the minimum time interval that the system can distinguish. Therefore, Expires / Hz is the cycle of this clock in seconds.
Function is the time reached a callback function, and its parameters are DATA in Timer_List. DATA This parameter assigns a value when initialization clock, generally assigns a Device structural pointer to its device.
In the preset time to the system call function, the system is cleared from the Time_List from the timed queue. So if you need to use a timing function, call add_timer () to add this Timer_List to the timing queue again in Function.
2.4.4 I / O
Access to I / O ports:
INLINE UNSIGNED INT INB (UNSIGNED SHORT Port)
INLINE UNSIGNED INT INB (UNSIGNED SHORT Port);
Inline void Outb (Char Value, Unsigned Short Port);
Inline void Outb_p (Char Value, Unsigned Short Port)
Definition in include / adm / o.h.
INB_P (), OUTB_P () and INB (), OUTB_P () differ than the former is a PAUSE to adapt to a slower I / O device at access I / O.
In order to prevent conflicts from accessing I / O, Linux provides control of port usage. Before using the port, you can check if the required I / O is being used. If not, mark the port as being used, and then release it. The system provides the following functions to do these work. INT CHECK_REGION (unsigned int extent);
Void Request_Region (unsigned int from, unsigned int extent, const char * name);
Void Release_Region (unsigned int extent);
The parameter from the Parameter from the I / O port used, the extent indicates the number of ports starting from the FROM. Name is the name of the device.
2.4.5 Interrupt Turn on
The system provides the ability to open and shut down response interrupts to drivers. Is two definitions in include / asm / system.h.
#DEfine CLI () __ASM__ __Volatile__ ("CLI": :)
#DEfine STI () __ASM__ __Volatile__ ("STI": :)
2.4.6 Printing Information
Similar to Printf () in a normal program, the driver is to output information using Printk (). Declaring in include / linux / kernel.h.
INT Printk (const char * fmt, ...);
Where FMT is a format string. ... is the parameter. It is the same as the Printf () format.
2.4.7 Register Driver
If you use the module (Module) to load the driver, you need to register your device to the system device table when the module is initialized. When using it, remove the device from the system from the system. Define the two functions in drivers / net / net_init.h to complete this work.
INT register_netdev (struct device * dev);
Void Unregister_netDev (Struct Device * dev);
DEV is the device structure pointer to which you want to register. At Register_netDev (), the DEV structure typically fills in the front of the front, that is, to init, which is temporarily available without initialization. The most important thing is the Name pointer and init method. Name pointer empty (NULL) or content / or NAME [0] is space (SPACE), the system is processed by Ethernet device. Ethernet equipment has a unified naming format, Ethx. It is related to the history of Ethernet to treat probably and Linux.
The init method must be provided, register_netdev () will call this method to let you detect and set hardware.
Register_netdev () returns 0 means success, non-0 is unsuccessful.
2.4.8 SK_Buff
Data transfer between the linux networks is through SK_BUFF. SK_BUFF provides a way to manage buffers, which is the key to efficient operation of Linux system networks. Each SK_BUFF includes some control methods and a data buffer. The control method is divided into two types according to the function. One is a method of controlling the entire Buffer chain.
The other is a method of controlling a data buffer. SK_BUFF organizes the form of a two-way linked list, according to the characteristics of the network application, the operation of the linked list is mainly to delete the elements of the linked head and add to the tail. SK_BUFF control
The method is very short to minimize system load. Translated from articles Written by Alan Cox
Common methods include:
.alloc_skb () applies for a SK_BUFF and initializes it. The return is the application-to-SK_BUFF.
.dev_alloc_skb () is similar to alloc_skb, after the buffer is applied, the 16-byte frame header space is retained. Mainly used in the Ethernet driver.
Kfree_skb () Releases a SK_BUFF. .skb_clone () Copys a SK_BUFF, but does not copy the data section.
.skb_copy () completely copies a SK_BUFF.
.skb_dequeue () Remove the first element from a SK_BUFF list. Returns the removed SK_BUFF, if the chain is returned to NULL. This is a common operation.
.skb_queue_head () puts an element in a SK_BUFF list header.
.skb_queue_tail () puts an element in a SK_BUFF linked list. This is also a common operation. The processing of network data is mainly to manage a first-first queue, SKB_QUEUE_TAIL ()
And SKB_DEQUEUE () completes this work.
.skb_insert () inserts an element before an element of the linked list.
.skb_append () Insert an element after an element of the linked list. Some protocols (such as TCP) use SKB_INSERT () and SKB_APPpend () when reorganizing data that is not reached in order.
.skb_reserve () keeps a piece of space in a buffer for a request SK_Buff. This space is generally used in the headspace of the next layer of protocol.
.skb_put () keeps a piece of space for the data in a buffer of a request SK_BUFF. in
After alloc_skb, the buffer of the application SK_BUFF is in an empty (free) state, and a TAIL pointer points to the free space, and TAIL will point to the buffer when TAIL is actually started. SKB_RESERVE ()
Apply a protocol header in the Free space, SKB_PUT () application data space. See the figure below.
.skb_push () Put the data space in the SK_BUFF buffer forward. That is, a part of the space in Head Room is part to Data Area.
.skb_pull () moves a part of the space in the Data Area in the SK_BUFF buffer to Head Room.
--------------------------------------------------
TAIL Room (Free) |
--------------------------------------------------
After alloc_skb ()
--------------------------------------------------
| Head room | Tail Room (Free) |
--------------------------------------------------
After SKB_RESERVE ()
--------------------------------------------------
Head Room | Data Area | Tail Room (Free) |
--------------------------------------------------
After SKB_PUT ()
--------------------------------------------------
| HEAD | SKB_ | Data | Tail Room (Free) |
| Room | Push | | | | |
| | Data Area | |
--------------------------------------------------
After SKB_PUSH ()
--------------------------------------------------
| HEAD | SKB_ | Data Area | Tail Room (Free) |
| | Pull | | | |
| Head room | | | | | |
--------------------------------------------------
After SKB_PULL ()
3. Writing a problem that needs attention in Linux network drivers
3.1 Interrupt Sharing
The Linux system is running several devices share the same interrupt. If you need sharing, you should specify the sharing method when you apply. The definition of the request_irq () call provided by the system: int Request_irq (unsigned IRQ,
Void (* Handler) (INT IRQ, Void * dev_id, struct pt_regs * regs),
Unsigned long Irqflags,
Const char * devname,
Void * dev_id);
If the shared interrupt, the IrqFlags sets the SA_SHIRQ property, which allows other devices to apply for the same interrupt. Note that all devices that use this interrupt must set this property in calling request_irq (). When the system calls each interrupt handler, you can find the corresponding device with the parameters of DEV_ID. When DEV_ID is set to the Device structure itself. The system processing shared interrupt is to call each interrupt handler in sequence with respective dev_id parameters.
3.2 Hardware to send a busy time
The main CPU's processing capability is generally faster than the network, so it often encounters the system data to be issued, but the previous package data network device has not been sent yet. Because the network device driver in Linux generally does not do a data cache, the data that cannot be sent is unsuccessful in the notification system, so there must be a mechanism to inform the system in time when the hardware is not busy, and the system will then send the following data.
Generally, the sending method has been described in the transmission method of the front device (hard_start_xmit), that is, if it is sent, set TBUSY 1. After processing the transmitted data, the TBUSY is cleared in the transmission end interrupt, and the notification system continues to be sent with the Mark_BH () call.
But when I realize my driver, this processing system seems to know that the hardware has been idle in time, that is, after mark_bh (), the system will wait for a while to send. There is a low transmission efficiency. The 2M line is only 10% less than the usage. The kernel version is 2.0.35.
My last implementation does not set TBusy, so that the system always thinks that the hardware is idle, but the report is unsuccessful. The system will have been trying to return. This is normal. However, the network driver in the kernel source code does not seem to be processed. I don't know where the crux is.
3.3 Flow Control (Flow Control)
The transmission and reception of network data require traffic control. These controls are implemented in the system and do not require the driver to work. Each device data structure has a parameter dev-> tx_queue_len, which indicates the most cached packets when sending. The Ethernet device (10 / 100Mbps) TX_QUEUE_LEN is typically set to 100, serial line (asynchronous serial port) is 10. In fact, if you can see the source code, you can know that DEV-> TX_QUE_LEN is not to apply for a space for cache these data. This parameter is just when receiving the data packet of the protocol layer, it is determined whether the data in the send queue is to the limit of TX_QUEUE_LEN to determine the package data does not add the send queue. The flow control of another aspect is the send window of a higher level protocol (there is a send window in the TCP protocol). The window size is reached, and the high-level agreement will not send data.
Receiving flow control is also divided into two levels. Netif_rx () cached packets are restricted. In addition, high-level protocols will have a maximum amount of data that is waiting for processing.
Send and receive flow control processing in DO_DEV_QUE_XMIT () and Netif_Rx () in NET / CORE / Dev.c.
3.4 debugging
Many Linux drivers are compiled into the kernel, forming a large kernel file. But for debugging, this is quite troublesome. The debug driver can be loaded with the Module mode. The driver for support module mode must provide two functions: int init_module (void) and void cleanup_module (void). INIT_MODULE () is called when this module is loaded, and in this function, you can register your device. INIT_MODULE () Returns 0 means success, return negative means failure. Cleanup_Module () calls, clears the resource when the driver is uninstalled, and invoke unregister_netdev (). The module can be loaded and unloaded. In version 2.0.xx, there is also a KernelD automatic load module, but Kernel has been canceled in 2.2.xx. Manually loading the insmod command, uninstall the RMMOD command, see the module in the kernel to use the lsmod command.
Compile the driver with GCC, the primary command line parameter -Dkernel -dmodule. And as the driver loaded by the module, only compile into an OBJ form (plus -c parameter). The compiled target file is placed under /lib/misc, loaded with Insmod in the startup file.
Fourth. Further reading
Linux programming materials can be available online. This is the benefits of open source code. And there is no "unapproved secret". The main information for the reference to the driver includes:
Linux kernel source code
<< The Linux Kernel Hackers Guide >> by Michael K. Johnson
<< Linux kernel module programing guide >> by Ori Pomerantz
You can choose a template as a start, the kernel source code has a network driver template,
DRIVERS / NET / SKELETON.C. The basic content of the driver is included. But this template is an object of Ethernet devices, and the processing of Ethernet has special "treatment" in the Linux system, so if it is not an Ethernet device, pay attention to some details, mainly in the initialization program.
Finally, more than the program written by others, the experience of listening to other developers is probably the most effective help.