Linux, as a powerful weapon to challenge Microsoft's monopoly, is increasingly being loved by everyone. I really hope she can grow up quickly in China. Post the program documents, I hope to discuss Linux technology and applications with you, and promote the popularization of Linux in China.
Linux operating system network driver programming
1. Overview of Linux system device drivers
1.1 Classification of Linux device drivers
1.2 Some basic concepts of writing a driver
2. Linux system network device driver
2.1 The structure of the network driver
2.2 Basic method of network driver
2.3 Data structure used in the network driver
2.4 Common system support
three. Problems that may be encountered in writing Linux network drivers
3.1 Interrupt sharing
3.2 Processing when the hardware sends busy
3.3 Flow control (flow control)
3.4 Debug
four. Further reading
Fives. Miscellaneous
1. Overview of Linux system device drivers
1.1 Classification of Linux device drivers
Linux device drivers occupies a large proportion of the Linux kernel source code, and the length of the source code is increasing day by day, mainly due to the increase of drivers. In the process of continuous upgrading of the Linux kernel, the structure of the driver is relatively stable. In the changes from 2.0.xx to 2.2.xx, some changes have been made to the writing of the driver, but the migration from 2.0.xx to 2.2.xx requires only a small amount of work.
Linux system devices are divided into three types: char device, block device and net work device. Character devices refer to devices that are not cached when accessed. Block device reads and writes are supported by cache, and block devices must be able to access (random access), character devices do not have this requirement. Typical character devices include mouse, keyboard, serial port, etc. Block devices mainly include hard disk and floppy disk devices, CD-ROM and so on. A file system must be on a block device to be installed into the operating system. Network equipment is handled specially in Linux. The Linux network system is mainly based on the socket mechanism of BSD unix. A special data structure (sk_buff) is defined for data transfer between the system and the driver. The system supports buffering of sent data and received data, provides a flow control mechanism, and provides support for multiple protocols.
1.2 Some basic concepts of writing a driver
No matter what operating system driver is, there are some general concepts. The support provided by the operating system to the driver is also roughly the same. The following briefly introduces some basic requirements of network device drivers.
1.2.1 Send and receive
This is the most basic function of a network device. What a network card does is nothing more than sending and receiving. So the driver must tell the system where your sending function is, and the system will call your sending program when there is data to send. There is also a driver program that directly manipulates the hardware, so the network hardware receives data that is the first to get this data, which is the driver program, which is responsible for the necessary processing of these raw data and then sends it to the system. Here, the operating system must provide two mechanisms, one is to find the sending function of the driver, and the other is to send the received data to the system by the driver.
1.2.2 Interrupt
Interruption plays an important role in modern computer architecture. The operating system must provide the driver with the ability to respond to interrupts. Generally, an interrupt handler is registered in the system. The operating system calls the driver's handler after the hardware interrupt occurs. Linux supports interrupt sharing, that is, multiple devices share an interrupt.
1.2.3 Clock
When implementing the driver, the clock is used in many places. For example, timeout processing in some protocols, hardware polling without interruption mechanism, etc. The operating system should provide a timing mechanism for the driver. Generally, the registered clock function is called back after a predetermined time has passed. In the network driver, if the hardware has no interrupt function, the timer can provide a polling method to access the hardware. Or it is the timeout retransmission required when implementing certain protocols.
2. Linux system network device driver
2.1 The structure of the network driver
All Linux network drivers follow a common interface. The design adopts an object-oriented approach. A device is an object (device structure), which has its own data and methods inside. The first parameter when the method of each device is called is the device object itself. This method can access its own data (similar to this reference in object-oriented programming). The most basic methods of a network device are 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 the variables in the device, and the application of system resources. The sending program is automatically called when there is data to be sent at the upper protocol layer of the driver. Generally, the sending data is not buffered in the driver, but the sending function of the hardware is used directly to send the data. The received data is generally notified through hardware interrupts. In the interrupt handler, fill the hardware frame information into a skbuff structure, and then call neTIf_rx() to pass it to the upper layer for processing.
2.2 Basic method of network driver
As an object, the network device provides some methods for system access. It is these methods with a unified interface that conceal the specific details of the hardware, allowing the system to use a unified form for access to various network devices, and achieve hardware independence.
The most basic method is explained below.
2.2.1 Initialization (iniTIalize)
The driver must have an initialization method. This initialization routine is called when the driver is loaded into the system. It does the following tasks. Testing Equipment. In the initialization program, you can check whether the hardware exists according to 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 hardware resources. For example, plug-and-play hardware can be configured at this time (Linux kernel does not support the PnP function very well, and this function can be completed in the driver). After configuring or negotiating the resources occupied by the hardware, you can apply to the system for these resources. Some resources can be shared with other devices, such as interrupts. Some cannot be shared, such as IO and DMA. Next you have to initialize the variables in the device structure. Finally, you can get the hardware to work officially.
2.2.2 Open
The open method is called when the network device is activated in the network device driver (that is, the device status is changed from down-->up). So in fact, a lot of work in initialize can be done here. Such as resource application, hardware activation. If dev-"open returns non-zero (error), the hardware status is still down. Another function of the open method is to prevent the device from being opened when the module is unloaded if the driver is loaded as a module. The MOD_INC_USE_COUNT macro should be adjusted in the open method.
2.2.3 Close (stop)
The close method does the opposite of open. Some resources can be released to reduce the burden on the system. close is called when the device state changes from up to down. In addition, if it is a driver loaded as a module, M OD_DEC_USE_COUNT should be called in 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 system calls the xmit of the driver, the data sent is placed in a sk_buff structure. Generally, the driver sends the data to the hardware. There are also some special devices such as loopback that composes the data into a receiving data and then sends it back to the system, or the dummy device directly discards the data.
If the transmission is successful, release sk_buff in the hard_start_xmit method and return 0 (successful transmission). If the device is temporarily unable to process, such as the hardware is busy, it returns 1. At this time, if dev-》tbusy is set to non-zero, the system considers the hardware to be busy and will not send again until dev-》tbusy is set to zero. The task of setting tbusy to 0 is generally completed by an interrupt. The hardware generates an interrupt after the end of the transmission. At this time, tbusy can be set to 0, and then the mark_bh() call can be used to notify the system that it can be sent again. In the case of unsuccessful sending, you can also not set dev-》tbusy to be non-zero, so that the system will continue to try to resend. If hard_start_xmit is sent unsuccessfully, do not release sk_buff. The data in the transmitted sk_buff already contains the frame header required by the hardware. So there is no need to fill the hardware frame header in the sending method, and the data can be directly submitted to the hardware for sending. sk_buff is locked (locked) to ensure that other programs will not access it.
2.2.5 Reception
The driver does not have a receiving method. The driver should notify the system when data is received. Generally, the device will generate an interrupt after receiving the data. In the interrupt handler, the driver applies for a piece of sk_buff (skb), reads the data from the hardware and puts it in the applied buffer. Next, fill in some information in sk_buff skb-》dev = dev, determine the protocol type of the received frame, and fill in skb-》protocol (multi-protocol support). Point the pointer skb-》m ac.raw to the hardware data and discard the hardware frame header (skb_pull). Also set skb-》pkt_type to indicate the data type of the second layer (link layer). Can be of the following types:
PACKET_BROADCAST: link layer broadcast
PACKET_MULTICAST: link layer multicast
PACKET_SELF: Frame sent to yourself
PACKET_OTHERHOST: Frames sent to others (there will be such frames in monitor mode)
Finally, netif_rx() is called to transfer the data to the protocol layer. The data in netif_rx() is put into the processing queue and then returned. The actual processing is after the interrupt returns, which can reduce the interruption time. After calling netif_rx(), the driver can no longer access the data buffer skb.
2.2.6 Hardware frame header (hard_header)
The hardware generally adds its own hardware frame header before the upper layer data is sent. For example, Ethernet has a 14-byte frame header. This frame header is added in front of the upper layer ip, ipx and other data packets. The driver provides a hard_header method, which is called by the protocol layer (ip, ipx, arp, etc.) before sending data. The length of the hardware frame header must be filled in dev-》hard_header_len, so that the protocol layer reserves the space for the hardware frame header before the data. In this way, the hard_header program only needs to call skb_push and fill in the hardware frame header correctly.
When calling hard_header at the protocol layer, the transmitted parameters include (2.0.xx): data sk_buff, device pointer, protocol, destination address (daddr), source address (saddr), data length (len). Do not use the parameter in s k_buff for the data length, because the data may not be fully organized when hard_header is called. If saddr is NULL, the default address (default) is used. If daddr is NULL, it indicates that the protocol layer does not know the hardware destination address. If hard_h eader completely fills in the hardware frame header, it returns the number of bytes added. If the information in the hardware frame header is not complete (for example, daddr is NULL, but the destination hardware address is required in the frame header. The typical situation is that the Ethernet requires address resolution (ar p)), then a negative number of bytes is returned. When hard_header returns a negative number, the protocol layer will do further work to build he ader. The current Linux system is to do arp (if hard_header returns positive, dev-》arp=1, it means that arp is not needed, and returns negative, dev-》arp=0, do arp).
The call to hard_header is in the handler of each protocol layer. Such as ip_output.
2.2.7 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 between 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 sending. The main parameters of the call include the pointer to the hardware frame header and the protocol layer address. If the driver can resolve the hardware address, it returns 1, if not, it returns 0.
The call to rebuild_header is in do_dev_queue_xmit() in net/core/dev.c.
2.2.8 Parameter settings and statistics
The driver also provides some methods for the system to set the device parameters and read information. Generally, only the super user (root) authority can set the device parameters. The setting methods are: dev-》set_mac_address() When the user calls the ioctl type as SIOCSIFHWADDR, it is necessary to set the mac address of the device. Generally, the setting of mac address does not make much sense.
dev-"set_config() When the type is SIOCSIFMAP when the user calls ioctl, the system will call the driver's set_config method. The user will pass an ifmap structure containing the required I/O, interrupt and other parameters.
dev-"do_ioctl()
If the type is between SIOCDEVPRIVATE and SIOCDEVPRIVATE+15 when the user calls ioctl, the system will call this method of the driver. It is generally dedicated data for setting the device. Reading information is also done through ioctl calls. In addition to this, the driver can also provide a dev-"get_stats method, which returns an enet_statistics structure, which contains statistical information sent and received. The processing of ioctl is in dev_ioctl() and dev_ifsioc() in net/core/dev.c.
@263.net "》
.3 Data structure used in the network driver
The most important thing is the data structure of the network equipment. Defined in include/linux/netdevice.h. Its comments have been
Sufficiently detailed.
struct device
{
/*
* This is the first field of the â€visible†part of this structure
* (Ie 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 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 start */
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 processing interrupts, and cleared to 0 after processing. */
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 these fields, but they are not part of the
usual set specified in Space.c. */
/* Some hardware can support multiple interfaces on one board, and if_port may be used. */
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 are internal to the system, and may change at
* will (read: may be cleaned up at will).
*/
/* These may be needed for future network-power-down code. */
/* trans_start records the time of the last successful transmission. Can be used to determine whether 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 many contents in flags, which are 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. Mainly indicate whether the hardware needs arp. Defined in
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 sending data buffer according to hard_header_len. */
unsigned short hard_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 char broadcastï¼»MAX_ADDR_LENï¼½; /* hw bcast add */
unsigned char pad; /* 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 PP 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_queue_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_ioctl) (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
kmalloc() and kfree() are declared in include/linux/kernel.h. Used to apply and release in kernel mode
RAM.
void *kmalloc(unsigned int len, int priority);
void kfree(void *__ptr);
Unlike malloc() in user mode, kmalloc() has a limit on the size of the application space. The length is a power of 2. can
There are also restrictions on the maximum length of the application. In addition, kmalloc() has a priority parameter, which can be GFP_K when used normally
ERNEL, if the GFP_ATOMIC parameter is used in the interrupt, because GFP_KERNEL is used, the caller may enter
The sleep state is not allowed when processing interrupts.
The memory released by kfree() must be requested by kmalloc(). If you know the size of the memory, you can also use kfree_s (
)freed.
2.4.2 request_irq(), free_irq()
This is the call for the driver to request and release the interrupt. Declared in include/linux/sched.h.
The definition of request_irq() call:
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 be applied for. On Intel platforms, the range is 0-15. handler is the interruption registered with the system
ç† function. This is a callback function. When an interrupt occurs, the system calls this function. The parameters passed in include the hardware
Break number, device id, register value. dev_id is the parameter dev passed to the system in request_irq below
_id. irqflags are some attributes of interrupt processing. The more important one is SA_INTERRUPT,
Indicate whether the interrupt handler is a fast handler (set SA_INTERRUPT) or a slow handler (do not set SA_IN)
TERRUPT). All interrupts are shielded when the fast handler is called. Slow handlers are not shielded. There is also a SA_
The SHIRQ attribute is set to run multiple devices sharing interrupts in the future. dev_id will be used when interrupt sharing. General settings
It is the device structure itself or NULL of this device. The interrupt handler can use dev_id to find the corresponding control
An interrupted device, or use irq2dev_map to find the device corresponding to the interrupt.
void free_irq (unsigned int irq, void *dev_id);
2.4.3 Clock
The clock processing is similar to an interrupt, and it also registers a time processing function. After a predetermined time has passed, the system will call this
Functions. Declared 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 * timer);
int del_timer(struct timer_list * timer);
void init_timer(struct timer_list * timer);
To use the clock, first declare a timer_list structure and call init_timer to initialize it.
Expires in the time_list structure indicates the period of this clock, and the unit is in jiffies.
jiffies is a global variable in Linux, representing time. Its unit varies with the hardware platform.
A constant HZ is defined in the system, which represents the number of minimum time intervals per second. Then the unit of jiffies is 1.
/HZ. The unit of Intel platform jiffies is 1/100 second, which is the minimum time interval that the system can distinguish. So
Expires/HZ is the period of this clock in seconds.
The function is the callback function after the time is up, and its parameter is the data in the timer_list. data this
The parameter is assigned when the clock is initialized, and is generally assigned to the device structure pointer of the device.
At the preset time, the system calls function, and the system clears this time_list from the timing queue at the same time. So as
If you need to use the timer function all the time, call add_timer() again in the function to add this timer_list
Timing queue.
2.4.4 I/O
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.
The difference between inb_p() and outb_p() and inb() and outb_p() is that the former has a pause when accessing I/O.
Should be slow I/O devices.
In order to prevent conflicts when accessing I/O, Linux provides control over port usage. Before using the port, you can
To check whether the required I/O is being used, if not, mark the port as being used and release it after use.
put. The system provides the following functions to do these tasks.
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 parameter from indicates the starting address of the I/O port used, and the extent indicates the number of ports starting from from. n
ame is the name of the device.
2.4.5 Interrupt opening and closing
The system provides the driver to open and close the ability to respond to interrupts. Are the two in include/asm/system.h
definition.
#define cli() __asm__ __volatile__ ("cli"::)
#define sti() __asm__ __volatile__ ("sti"::)
2.4.6 Print information
Similar to printf() in ordinary programs, the driver uses printk() to output information. In include/linux/ke
Declared in rnel.h.
int printk(const char* fmt, ...);
Where fmt is the format string. . .. is the parameter. They are all in the same format as printf().
2.4.7 Registering the driver
If you use the module method to load the driver, you need to register the device to the system device when the module is initialized
Go inside. When it is no longer in use, remove the device from the system. Two defined in drivers/net/net_init.h
The function does this work.
int register_netdev(struct device *dev);
void unregister_netdev(struct device *dev);
dev is the device structure pointer to be registered into the system. In register_netdev(), the dev structure generally fills in the front
Item 11, that is, to init, the latter can not be initialized temporarily. The most important is the name pointer and the init method. name
If the pointer is NULL or the content is or name [0] is space (space), the system will treat your device as an Ethernet device.
备Handle. Ethernet devices have a unified naming format, ethX. The special treatment of Ethernet is probably related to the history of Linux.
History related.
The init method must be provided, and register_netdev() will call this method to allow you to detect and set the hardware.
register_netdev() returns 0 to indicate success, non-zero unsuccessful.
2.4.8 sk_buff
The data transmission between the various layers of the Linux network is through sk_buff. sk_buff provides a set of methods to manage the buffer,
It is the key to the efficient operation of the Linux system network. 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 to control the entire buffer chain,
The other is to control the data buffer. sk_buff is organized into a doubly linked list, according to the characteristics of network applications
, The operation of the linked list is mainly to delete the element at the head of the linked list and add to the end of the linked list. Control of sk_buff
The methods are short and small to minimize system load. (Translated from article written by Alan Cox
)
Commonly used methods include:
.alloc_skb() Apply for a sk_buff and initialize it. The return is the applied sk_buff.
.dev_alloc_skb() is similar to alloc_skb, after applying for a buffer, 16 bytes of frame header space is reserved. Mainly used
In the Ethernet driver.
.kfree_skb() Free a sk_buff.
.skb_clone() Copies a sk_buff, but does not copy the data part.
.skb_copy() completely copies a sk_buff.
.skb_dequeue() Take the first element from a sk_buff list. Return the removed sk_buff, if the linked list
NULL is returned if it is empty. This is a commonly used operation.
.skb_queue_head() Put an element in the head of a sk_buff list.
.skb_queue_tail() Put an element at the end of a sk_buff list. This is also a commonly used operation. The internet
Data processing is mainly the management of a first-in first-out queue, skb_queue_tail()
And skb_dequeue() complete this work.
.skb_insert() Insert an element before an element of the linked list.
.skb_append() Insert an element after an element in the linked list. Some protocols (such as TCP) do not arrive in order
Skb_insert() and skb_append() are used when data is reorganized.
.skb_reserve() Reserve a space in a buffer of sk_buff that has been applied for. This space is generally used
Do the head space of the next layer of agreement.
.skb_put() Reserve a space for data in an applied sk_buff buffer. in
After alloc_skb, the buffers of the applied sk_buff are all in the free state, and there is a tail pointer.
To the free space, the tail actually points to the head of the buffer at the beginning. skb_reserve()
Apply for protocol header space in free space, skb_put() applies for data space. See the figure below.
.skb_push() Move the data space in the sk_buff buffer forward. That is to move a part of the space in the head room to
Data area.
.skb_pull() Move a part of the data area in the sk_buff buffer to the 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()
three. Issues Needing Attention in Writing Linux Network Drivers
3.1 Interrupt sharing
The Linux system runs several devices sharing the same interrupt. If sharing is required, specify the sharing method when applying.
The definition of request_irq() call provided by the system:
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);
If the interrupt is shared, irqflags sets the SA_SHIRQ attribute, which allows other devices to apply for the same interrupt. need
Note that all devices that use this interrupt must set this attribute when calling request_irq(). The system is calling back
For each interrupt handler, you can use the dev_id parameter to find the corresponding device. Generally dev_id is set to dev
The ice structure itself. The system handles the shared interrupt by calling each interrupt handler in turn with its own dev_id parameter.
3.2 Processing when the hardware sends busy
The processing power of the main CPU is generally faster than the network transmission, so it is often encountered that the system has data to send, but the last packet of data
The network device has not finished sending yet. Because network device drivers in Linux generally do not cache data, they cannot be sent
The data is to notify the system that the transmission is unsuccessful, so there must be a mechanism to notify the system in time when the hardware is not busy.
Send the following data.
Generally, the processing of sending busy is described in the sending method (hard_start_xmit) of the previous device, that is, if sending
Send busy, set tbusy to 1. After processing the sending data, clear tbusy in the sending end interrupt, and use mark_bh() at the same time
Call the notification system to continue sending.
However, when implementing my driver, I found that such a processing system does not seem to know that the hardware is empty in time.
It is idle, that is, after mark_bh(), the system will wait for a while before sending. The sending efficiency is very low. 2M line
The usage rate of the road is less than 10%. The kernel version is 2.0.35.
My final implementation is not to set tbusy to 1, so that the system always thinks that the hardware is idle, but the report is not sent successfully. system
Will always try to resend. This process will run normally. But going through the network driver in the kernel source code, it seems
No such treatment. I don't know where the crux is.
3.3 Flow control (flow control)
Both the sending and receiving of network data require flow control. These controls are implemented in the system and do not require a driver
work. Each device data structure has a parameter dev- "tx_queue_len, this parameter indicates the most
Multi-buffered packets. In the Linux system, the Ethernet device (10/100Mbps) tx_queue_len is generally set to 100
, The serial line (asynchronous serial port) is 10. In fact, if you look at the source code, you can know that dev-"tx_queue_len is set and
Space is not allocated for caching these data. This parameter only judges the sending queue when receiving a data packet from the protocol layer
Whether the data in the data reaches the limit of tx_queue_len to determine whether this packet of data is added to the send queue. send
Another aspect of flow control is the sending window of higher-level protocols (there is a sending window in the TCP protocol). Reached the window size
If it is small, the high-level protocol will no longer send data.
Receive flow control is also divided into two levels. Netif_rx() has a limit on the data packets cached. In addition, the high-level agreement will also have a maximum
Large amount of data waiting to be processed.
The sending and receiving flow control processing is in do_dev_queue_xmit() and netif_rx() in net/core/dev.c.
3.4 Debug
Many Linux drivers are compiled into the kernel to form a large kernel file. But for debugging, this is
Quite troublesome. The debug driver can be loaded in module mode. The driver supporting the module mode must be provided
Two functions: int init_module (void) and void cleanup_module (void). init_module() is adding
It is called when this module is loaded, and the device can be registered with register_netdev() in this function. init_module() returns
0 means success, return negative means failure. cleanup_module()在驱动程åºè¢«å¸è½½æ—¶è°ƒç”¨ï¼Œæ¸…除å 用的
资æºï¼Œè°ƒç”¨unregister_netdev()。
模å—å¯ä»¥åŠ¨æ€åœ°åŠ è½½ã€å¸è½½ã€‚在2.0.xx版本里,还有kerneldè‡ªåŠ¨åŠ è½½æ¨¡å—,但是2.2.x
xä¸å·²ç»å–消了kerneldã€‚æ‰‹å·¥åŠ è½½ä½¿ç”¨insmod命令,å¸è½½ç”¨rmmodå‘½ä»¤ï¼Œçœ‹å†…æ ¸ä¸çš„模å—
用lsmod命令。
编译驱动程åºç”¨gcc,主è¦å‘½ä»¤è¡Œå‚æ•°-DKERNEL -DMODULE。并且作为模å—åŠ è½½çš„é©±åŠ¨ç¨‹
åºï¼Œåªç¼–译æˆobjå½¢å¼ï¼ˆåŠ -cå‚æ•°ï¼‰ã€‚ç¼–è¯‘å¥½çš„ç›®æ ‡æ–‡ä»¶æ”¾åœ¨/lib/modules/2.x.xx/misc下
,在å¯åŠ¨æ–‡ä»¶é‡Œç”¨insmodåŠ è½½ã€‚
四。进一æ¥çš„阅读
Linux程åºè®¾è®¡èµ„æ–™å¯ä»¥ä»Žç½‘上获得。这就是开放æºä»£ç 的好处。
Connector And Tool,Optic Cable Kits ,Metal Line Fitting ,Optical Fiber Cleaner Box
CIXI LANGUANG PHOTOELECTRIC TECHNOLOGY CO..LTD , https://www.cxblueray.com