0、引入

前面,总结了网络模型等的相关知识(https://xingxingzhihuo.blog.csdn.net/article/details/94360079)

对于网卡驱动程序,可以不考虑网络协议多层的具体实现,而仅仅关注对网卡硬件的驱动,通过TCP/IP协议栈接口及linux网络接口将网络数据发送出去并接收外部发来的数据,即驱动网卡进行网络数据的发包和收包。与字符设备和块设备不同,网络设备不会在/dev下产生设备节点,应用程序最终使用套接字完成与网络设备的接口。

1、网卡驱动程序框架

1.1 整体框架

网卡驱动无需关注具体的网络协议栈,仅关注硬件相关的操作及调用内核接口完成网卡的注册及收发包函数的具体实现即可。网卡驱动框架可简单的表述为如下图:

在《Linux设备驱动开发详解》一书中,将网络设备驱动划分为4层,分别为网络协议接口层,网络设备接口层,设备驱动功能层,网络设备与媒介层,这些层次的划分是软件设计的架构以及更好的进行软硬件的解耦,可以被看作为网卡驱动存在的几个重要组成部分。下图来自该书:

1.2 网卡驱动层结构

1.2.1 网络协议接口层(sk_buff)

使网卡驱动与网络协议层交互提供接口。上层协议独立于具体的设备。通过dev_queue_xmit()函数发送数据,并通过 netif_rx()函数接收数据。

可以看出与上层网络协议交互通过sk_buff( include/linux/skbuff.h)结构体完成,在该书中被意为“套接字缓冲区”,是Linux 网络子系统数据传递的 “ 中枢神经 ” 。待发送数据在sk_buff中,网络协议在各层中添加不同的头部信息,并以此传输至网卡驱动中进行发送,同时网卡驱动接收到数据时,将从网络媒介收到的数据包转换为sk_buff传递给上层协议,各层剥去相应的协议头后将数据传输给最终的用户APP。

head 和 end 指向缓冲区的头部和尾部,data 和 tail 指向实际数据的头部和尾部。每一层会在 head 和 data之间填充协议头,或者在tail和end之间添加新的协议数据。

/** *    struct sk_buff - socket buffer* @next: Next buffer in list*    @prev: Previous buffer in list*    @tstamp: Time we arrived*  @sk: Socket we are owned by*   @dev: Device we arrived on/are leaving by* @cb: Control buffer. Free for use by every layer. Put private vars here*   @_skb_refdst: destination entry (with norefcount bit)* @sp: the security path, used for xfrm* @len: Length of actual data*   @data_len: Data length*    @mac_len: Length of link layer header* @hdr_len: writable header length of cloned skb*    @csum: Checksum (must include start/offset pair)*  @csum_start: Offset from skb->head where checksumming should start* @csum_offset: Offset from csum_start where checksum should be stored*  @priority: Packet queueing priority*   @local_df: allow local fragmentation*  @cloned: Head may be cloned (check refcnt to be sure)* @ip_summed: Driver fed us an IP checksum*  @nohdr: Payload reference only, must not modify header*    @nfctinfo: Relationship of this skb to the connection* @pkt_type: Packet class*   @fclone: skbuff clone status*  @ipvs_property: skbuff is owned by ipvs*   @peeked: this packet has been seen already, so stats have been*        done for it, don't do them again*  @nf_trace: netfilter packet trace flag*    @protocol: Packet protocol from driver*    @destructor: Destruct function*    @nfct: Associated connection, if any*  @nfct_reasm: netfilter conntrack re-assembly pointer*  @nf_bridge: Saved data about a bridged frame - see br_netfilter.c* @skb_iif: ifindex of device we arrived on* @tc_index: Traffic control index*  @tc_verd: traffic control verdict* @rxhash: the packet hash computed on receive*  @queue_mapping: Queue mapping for multiqueue devices*  @ndisc_nodetype: router type (from link layer)*    @ooo_okay: allow the mapping of a socket to a queue to be changed* @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport*        ports.* @wifi_acked_valid: wifi_acked was set* @wifi_acked: whether frame was acked on wifi or not*   @no_fcs:  Request NIC to treat last 4 bytes as Ethernet FCS*   @dma_cookie: a cookie to one of several possible DMA operations*       done by skb DMA functions*  @secmark: security marking*    @mark: Generic packet mark*    @dropcount: total number of sk_receive_queue overflows*    @vlan_tci: vlan tag control information*   @transport_header: Transport layer header* @network_header: Network layer header* @mac_header: Link layer header*    @tail: Tail pointer*   @end: End pointer* @head: Head of buffer* @data: Data head pointer*  @truesize: Buffer size*    @users: User count - see {datagram,tcp}.c*/struct sk_buff {/* These two members must be first. */struct sk_buff        *next;struct sk_buff        *prev;ktime_t           tstamp;struct sock      *sk;struct net_device   *dev;/** This is the control buffer. It is free to use for every* layer. Please put your private variables there. If you* want to keep them across layers you have to do a skb_clone()* first. This is owned by whoever has the skb queued ATM.*/char           cb[48] __aligned(8);unsigned long       _skb_refdst;
#ifdef CONFIG_XFRMstruct    sec_path    *sp;
#endifunsigned int      len,data_len;__u16          mac_len,hdr_len;union {__wsum       csum;struct {__u16  csum_start;__u16    csum_offset;};};__u32           priority;kmemcheck_bitfield_begin(flags1);__u8          local_df:1,cloned:1,ip_summed:2,nohdr:1,nfctinfo:3;__u8         pkt_type:3,fclone:2,ipvs_property:1,peeked:1,nf_trace:1;kmemcheck_bitfield_end(flags1);__be16           protocol;void           (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)struct nf_conntrack  *nfct;
#endif
#ifdef NET_SKBUFF_NF_DEFRAG_NEEDEDstruct sk_buff        *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTERstruct nf_bridge_info *nf_bridge;
#endifint           skb_iif;__u32           rxhash;__u16            vlan_tci;#ifdef CONFIG_NET_SCHED__u16           tc_index;   /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT__u16          tc_verd;    /* traffic control verdict */
#endif
#endif__u16         queue_mapping;kmemcheck_bitfield_begin(flags2);
#ifdef CONFIG_IPV6_NDISC_NODETYPE__u8           ndisc_nodetype:2;
#endif__u8          ooo_okay:1;__u8         l4_rxhash:1;__u8            wifi_acked_valid:1;__u8         wifi_acked:1;__u8           no_fcs:1;/* 9/11 bit hole (depending on ndisc_nodetype presence) */kmemcheck_bitfield_end(flags2);#ifdef CONFIG_NET_DMAdma_cookie_t     dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK__u32          secmark;
#endifunion {__u32      mark;__u32      dropcount;__u32     avail_size;};sk_buff_data_t     transport_header;sk_buff_data_t     network_header;sk_buff_data_t       mac_header;/* These elements must be at the end, see alloc_skb() for details.  */sk_buff_data_t     tail;sk_buff_data_t     end;unsigned char       *head,*data;unsigned int        truesize;atomic_t       users;
};

常用的操作函数:

分配:

struct sk_buff *alloc_skb(unsigned int len, gfp_t priority);

struct sk_buff *dev_alloc_skb(unsigned int len);

释放:

void kfree_skb(struct sk_buff *skb);

void dev_kfree_skb(struct sk_buff *skb);

void dev_kfree_skb_irq(struct sk_buff *skb);

void dev_kfree_skb_any(struct sk_buff *skb);

变更:

unsigned char *skb_put(struct sk_buff *skb, unsigned int len);//在缓冲区尾部增加数据:

unsigned char *skb_push(struct sk_buff *skb, unsigned int len);//在缓冲区开头增加数据

static inline void skb_reserve(struct sk_buff *skb, int len);

/* 分配一个全新的 sk_buff ,接着调用 skb_reserve ()腾出头部空间,之后调用 skb_put ()腾出数据空
间,然后把数据复制进来,最后把 sk_buff 传给协议栈。*/skb=alloc_skb(len+headspace, GFP_KERNEL);skb_reserve(skb, headspace);skb_put(skb,len);memcpy_fromfs(skb->data,data,len);pass_to_m_protocol(skb);

1.2.2 网络设备接口层(net_device

向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体 struct net_device ,该结构体是设备驱动功能层中各函数的容器。实际上,网络设备接口层从宏观上规划了具体操作硬件的设备驱动功能层的结构。

struct net_device(include\linux\netdevice.h)

struct net_device {/** 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* of the interface.*///全局信息char            name[IFNAMSIZ];//网络设备的名称//硬件信息unsigned long     mem_end;    /* shared mem end   */unsigned long     mem_start;  /* shared mem start */unsigned long     base_addr;  /* device I/O address   */unsigned int      irq;        /* device IRQ number    */...unsigned char      dma;        /* 分配给设备的DMA channel        *///接口信息unsigned int        mtu;    /* interface MTU value      */unsigned short        type;   /* interface hardware type  */unsigned short        hard_header_len;    /* 网络设备硬件头长度    */.../* Interface address info used in eth_type_trans() */unsigned char     *dev_addr;  /* MAC地址, (before bcastbecause most packets areunicast) */unsigned int      flags;  /* 网络接口的标志 (a la BSD)   *///操作函数/* Management operations */const struct net_device_ops *netdev_ops;//网络设备硬件操作的集合,重要,如open closeconst struct ethtool_ops *ethtool_ops;//辅助成员unsigned long      trans_start;    /* 最后数据包开始发送的时间戳Time (in jiffies) of last Tx    */unsigned long     last_rx; //最后一次接收到数据包的时间戳 jiffies
}

1.2.3 设备驱动功能层(net_device_ops

网络设备接口层 net_device 数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过 hard_start_xmit ()函数启动发送操作,并通过网络设备上的中断触发接收操作。

struct net_device_ops (include\linux\netdevice.h)

struct net_device_ops {int           (*ndo_init)(struct net_device *dev);void            (*ndo_uninit)(struct net_device *dev);//打开网络设备,获取IO地址,IRQ,DMA通道等int            (*ndo_open)(struct net_device *dev);int         (*ndo_stop)(struct net_device *dev);//数据包发送函数,传递sk_buff指针,获取上层传递下来的数据包。netdev_tx_t        (*ndo_start_xmit) (struct sk_buff *skb,struct net_device *dev);u16          (*ndo_select_queue)(struct net_device *dev,struct sk_buff *skb);void            (*ndo_change_rx_flags)(struct net_device *dev,int flags);void           (*ndo_set_rx_mode)(struct net_device *dev);//设置MAC地址int         (*ndo_set_mac_address)(struct net_device *dev,void *addr);int           (*ndo_validate_addr)(struct net_device *dev);int            (*ndo_do_ioctl)(struct net_device *dev,struct ifreq *ifr, int cmd);//配置接口,也可用于改变设备io和中断号等int         (*ndo_set_config)(struct net_device *dev,struct ifmap *map);int         (*ndo_change_mtu)(struct net_device *dev,int new_mtu);int           (*ndo_do_ioctl)(struct net_device *dev,struct ifreq *ifr, int cmd);//获取网络设备当前状态,net_device_stats保存了详细了网络数据流量信息,如发包接收包数,字节数等struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
}

1.2.4 网络设备与媒介层

完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数在物理上驱动。对于 Linux 系统而言,网络设备和媒介都可以是虚拟的。

1.2.5 总结

网卡驱动框架需要弄清几个重要的结构体:

  • 数据包结构sk_buff

  • 网络设备描述net_device
  • 网络设备操作函数结构体net_device_ops

1.3 网卡驱动常用API函数

1.3.1 网络设备注册与注销

注册:

int register_netdev(struct net_device *dev);

注销:

void unregister_netdev(struct net_device *dev);

net_device生产:

#define alloc_netdev(sizeof_priv, name, setup)  alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)
#define alloc_etherdev(sizeof_priv)  alloc_etherdev_mq(sizeof_priv, 1)struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,void (*setup)(struct net_device *),unsigned int txqs, unsigned int rxqs);

alloc_netdev_mqs()函数生成一个 net_device 结构体,对其成员赋值并返回该结构体的指针。第一个参数为设备私有成员的大小,第二个参数为设备名,第三个参数为 net_device 的 setup ()函数指针,第四、五个参数为要分配的发送和接收子队列的数量。

网卡驱动注册函数的框架:

static int xxx_register(void)
{.../*  分配 net_device 结构体并对其成员赋值 */xxx_dev = alloc_netdev(sizeof(struct xxx_priv), "sn%d", xxx_init);if (xxx_dev == NULL)... /*  分配 net_device 失败 *//*  注册 net_device 结构体 */if ((result = register_netdev(xxx_dev)))...
}static void xxx_unregister(void)
{.../*  注销 net_device 结构体 */unregister_netdev(xxx_dev);/*  释放 net_device 结构体 */free_netdev(xxx_dev);
}

1.3.2 网络设备初始化

XXX_init()函数

  • 设备私有信息初始化,xxxx_priv
  • 硬件资源初始化,xxx_hw_init,硬件存在,硬件配置,硬件资源申请,IO申请等
  • 以太网初始化,ether_setup
  • net_device设备函数指针初始化,netdev_ops,ethtool_ops
  • 初始化设备私有数据,netdev_priv()

1.3.3 网络设备打开和关闭

网络设备的打开函数需要完成如下工作:

  • 使能设备使用的硬件资源,申请 I/O 区域、中断和 DMA 通道等。
  • 调用 Linux 内核提供的 netif_start_queue ()函数,激活设备发送队列。

网络设备的关闭函数需要完成如下工作:

  • 调用 Linux 内核提供的 netif_stop_queue ()函数,停止设备传输包。
  • 释放设备所使用的 I/O 区域、中断和 DMA 资源。

Linux内核提供的API

void netif_start_queue(struct net_device *dev);  //使能发送队列
void netif_stop_queue (struct net_device *dev); //禁止发送

网络设备打开模板:

static int xxx_open(struct net_device *dev)
{/*  申请端口、 IRQ 等,类似于 fops->open */ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);...netif_start_queue(dev);...
}

网络设备关闭模板

static int xxx_release(struct net_device *dev)
{/*  释放端口、 IRQ 等,类似于 fops->close */free_irq(dev->irq, dev);...netif_stop_queue(dev); /* can't transmit any more */...
}

1.3.4 数据包发送

Linux发包的时候调用hard_start_transmit()函数,在设备初始化的时候,这个函数指针需被初始化以指向设备的 xxx_tx ()函数。

网络设备驱动完成数据包发送的流程如下:

  1. 网络设备驱动程序从上层协议传递过来的 sk_buff 参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区。
  2. 对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度 ETH_ZLEN ,则给临时缓冲区的末尾填充 0 。
  3. 设置硬件的寄存器,驱使网络设备进行数据发送操作

netif_wake_queue ()和 netif_stop_queue ()是数据发送流程中要调用的两个非常重要的函数,分别用于唤醒和阻止上层向下传送数据包,它们的原型定义于 include/linux/netdevice.h 中,如下:
static inline void netif_wake_queue(struct net_device *dev); //使能应用发送
static inline void netif_stop_queue(struct net_device *dev); //阻值应用继续发送,如发送队列满,还没发送完成等

1.3.5 网络数据接收

中断引发设备中断处理函数被调用。中断处理函数判断中断类型,如果为接收中断,则读取接收到的数据,分配 sk_buffer 数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()函数将 sk_buffer 传递给上层协议。

static void xxx_interrupt(int irq, void *dev_id)
{...switch (status &ISQ_EVENT_MASK) {case ISQ_RECEIVER_EVENT:/*  获取数据包 */xxx_rx(dev);break;/*  其他类型的中断 */
}
static void xxx_rx(struct xxx_device *dev)
{...length = get_rev_len (...);/*  分配新的套接字缓冲区 */skb = dev_alloc_skb(length + 2);skb_reserve(skb, 2); /*  对齐 */skb->dev = dev;/*  读取硬件上接收到的数据 */insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1);if (length &1)skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);/*  获取上层协议类型 */skb->protocol = eth_type_trans(skb, dev);/*  把数据包交给上层 */netif_rx(skb);/*  记录接收时间戳 */dev->last_rx = jiffies;...
}

1.3.6 网络连接状态

网络适配器硬件电路可以检测出链路上是否有载波,载波反映了网络的连接是否正常。网络设备驱动可以通过netif_carrier_on ()和 netif_carrier_off ()函数改变设备的连接状态,如果驱动检测到连接状态发生变化,也应该以 netif_carrier_on ()和 netif_carrier_off()函数显式地通知内核。另一个函数 netif_carrier_ok ()可用于向调用者返回链路上的载波信号是否存在。

void netif_carrier_on(struct net_device *dev);
void netif_carrier_off(struct net_device *dev);
int netif_carrier_ok(struct net_device *dev);

驱动中一般有一个static void xxx_timer(unsigned long data)函数定时的监测状态并上报,在open初始化定时器,在定时器中反复启动定时器。

1.3.7 参数设置和统计数据

驱动中提供ioctl函数接口,如传入SIOCSIFHWADDR参数时可设置MAC地址。ioctl->set_mac_address()。

传入SIOCSIFMAP(ifconfig),调用set_config();

将收发包计数等统计信息修改在net_device_stats结构体中。

2、虚拟网卡驱动程序

来自韦东山老师的虚拟网卡驱动例程。可以实现ping自己,以及构造的虚拟ping包过程。


/** 参考 drivers\net\cs89x0.c*/#include <linux/module.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/ip.h>#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>static struct net_device *vnet_dev;static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)
{/* 参考LDD3 */unsigned char *type;struct iphdr *ih;__be32 *saddr, *daddr, tmp;unsigned char  tmp_dev_addr[ETH_ALEN];struct ethhdr *ethhdr;struct sk_buff *rx_skb;// 从硬件读出/保存数据/* 对调"源/目的"的mac地址 */ethhdr = (struct ethhdr *)skb->data;memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);/* 对调"源/目的"的ip地址 */    ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));saddr = &ih->saddr;daddr = &ih->daddr;tmp = *saddr;*saddr = *daddr;*daddr = tmp;//((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) *///((u8 *)daddr)[2] ^= 1;type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);//printk("tx package type = %02x\n", *type);// 修改类型, 原来0x8表示ping*type = 0; /* 0表示reply */ih->check = 0;         /* and rebuild the checksum (ip needs it) */ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);// 构造一个sk_buffrx_skb = dev_alloc_skb(skb->len + 2);skb_reserve(rx_skb, 2); /* align IP on 16B boundary */  memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);/* Write metadata, and then pass to the receive level */rx_skb->dev = dev;rx_skb->protocol = eth_type_trans(rx_skb, dev);rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */dev->stats.rx_packets++;dev->stats.rx_bytes += skb->len;// 提交sk_buffnetif_rx(rx_skb);
}static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev)
{static int cnt = 0;printk("virt_net_send_packet cnt = %d\n", ++cnt);/* 对于真实的网卡, 把skb里的数据通过网卡发送出去 */netif_stop_queue(dev); /* 停止该网卡的队列 *//* ...... */           /* 把skb的数据写入网卡 *//* 构造一个假的sk_buff,上报 */emulator_rx_packet(skb, dev);dev_kfree_skb (skb);   /* 释放skb */netif_wake_queue(dev); /* 数据全部发送出去后,唤醒网卡的队列,一般在发送完成中断里面唤醒 *//* 更新统计信息 */dev->stats.tx_packets++;dev->stats.tx_bytes += skb->len;return 0;
}static int virt_net_init(void)
{/* 1. 分配一个net_device结构体 */vnet_dev = alloc_netdev(0, "vnet%d", ether_setup);;  /* alloc_etherdev *//* 2. 设置 */vnet_dev->hard_start_xmit = virt_net_send_packet;/* 设置MAC地址 */vnet_dev->dev_addr[0] = 0x08;vnet_dev->dev_addr[1] = 0x89;vnet_dev->dev_addr[2] = 0x89;vnet_dev->dev_addr[3] = 0x89;vnet_dev->dev_addr[4] = 0x89;vnet_dev->dev_addr[5] = 0x11;/* 设置下面两项才能ping通 */vnet_dev->flags           |= IFF_NOARP;vnet_dev->features        |= NETIF_F_NO_CSUM;   /* 3. 注册 *///register_netdevice(vnet_dev);register_netdev(vnet_dev);return 0;
}static void virt_net_exit(void)
{unregister_netdev(vnet_dev);free_netdev(vnet_dev);
}module_init(virt_net_init);
module_exit(virt_net_exit);MODULE_AUTHOR("thisway.diy@163.com,17653039@qq.com");
MODULE_LICENSE("GPL");

3、移植DM9000网卡驱动程序

【linux驱动】网卡驱动程序相关推荐

  1. linux网卡驱动 pdf,Linux下网卡驱动程序.pdf

    zekairecv 于 2015-10-04 00:58:57发表: 谢谢 weilee1 于 2015-04-19 17:41:05发表: 看看 雪语阑风 于 2014-12-04 11:03:39 ...

  2. linux网卡驱动开发视频,Linux下网卡驱动程序的开发.doc

    Linux下网卡驱动程序的开发 论文题目:Linux下网卡驱动程序的开发 专 业: 年 级: 学生学号: 学生姓名: 指导教师: 完成时间: Linux下网卡驱动程序的开发 八年经验 专业指导毕业设计 ...

  3. Linux驱动(驱动程序开发、驱动框架代码编译和测试)

    目录 什么是驱动: 设备分类: 驱动认知: 字符设备驱动工作原理 字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系 驱动程序开发步骤 基于驱动框架的代码开发 驱动模块代码编译和测试 加载 ...

  4. Linux DM9000网卡驱动程序完全分析

    版权声明: 可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息. 说明1:本文分析基于内核源码版本为linux-2.6.31 说明2:本文在理解了linux中总线.设备和驱动模型的基础上 ...

  5. Linux通过网卡驱动程序和版本号的信息

    检查卡制造商和信号 查看基本信息:lspci 查看详情:lspci -vvv   # 3小作文v 查看卡信息:lspci | grep Ethernet 查看网卡驱动 查看网卡驱动信息:lspci - ...

  6. linux驱动LCD 驱动程序代码编写

    转自 在编写代码之前我们先来看一看原理图 引脚说明: VCLK:发出lcd时钟信号,每来一个时钟,就会在屏幕上显示一个像素      --GPC1  :配置为lcd引脚 VLINE:发出lcd行扫描信 ...

  7. linux 7.0安装网卡驱动,安装或更新CentOS6.7平台的网卡驱动程序

    基于Linux平台安装或更新网卡驱动程序与Windows平台相差不大,首先查阅出主机网卡的具体型号.Windows平台可以借助鲁大师等硬件检测工具查看网卡,Linux平台有适用的命令lspci.eth ...

  8. linux 网卡驱动升级,安装或更新CentOS平台的网卡驱动程序

    基于Linux平台安装或更新网卡驱动程序与Windows平台相差不大,首先查阅出主机网卡的具体型号.Windows平台可以借助鲁大师等硬件检测工具查看网卡,Linux平台有适用的命令lspci.eth ...

  9. 基于linux的千兆网卡驱动程序实现及数据传输效率优化,嵌入式Linux下网卡驱动的实现与数据转发性能优化分析...

    摘要: 伴随着互联网的快速发展和后PC时代的到来,嵌入式系统已逐步成为当今IT产业的焦点之一,广阔的市场前景使嵌入式系统获得了空前的发展机遇.由于Linux操作系统具有代码开放.内核可裁减.网络功能强 ...

最新文章

  1. 不需要程序员,产品经理也能10分钟打造一个深度学习模型
  2. 右值引用 移动构造函数 移动语义
  3. vue+node+mongodb 搭建一个完整博客
  4. android源码出现的@字符意义总结
  5. VTK:可视化算法之CutStructuredGrid
  6. php mysql 字段备注_MySQL下读取 表/字段 的说明备注信息
  7. NASA WorldWind1.4.0源代码调试总结
  8. chrom禁用浏览器回退按钮不管用_什么?作为程序员你都工作了还不会用Git
  9. 记一次 .NET 某流媒体独角兽 API 句柄泄漏分析
  10. C语言不调用库函数画直线
  11. MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解...
  12. Android 日志工具包
  13. python中label怎么绑定变量_Kivy将标签文本绑定到变量(仅限Python)
  14. ccf 路径解析 java_CCF 201604-3 路径解析
  15. 华硕k5555l拆解图解_华硕k50id的拆机过程详解【图文教程】
  16. python鼠标点击事件event_opencv-python教程学习系列5-处理鼠标事件
  17. python中标点符号大全及名字_标点符号大全及名字0919.史上最全标点符号英语读法...
  18. 如何搞出一个大型网站
  19. Keras下载数据失败,本地导入
  20. VulnHub靶场LupinOne

热门文章

  1. 操作系统测试题(第7,8,9单元)
  2. OSGEarth模型点击事件
  3. Silverlight游戏研发手记:(一)差集运算在SLG战斗范围设定中的应用
  4. Hadoop——(Hadoop框架,Hadoop的优缺点,Hadoop1.x和2.x的版本区别,Hadoop架构,Hadoop目录结构)
  5. C语言递归:斐波那契数列
  6. 【MybatisPlus】表名或字段名是Mysql关键字如何解决
  7. Cloudera Manager部署StreamSets
  8. [Codefroces 1230E] Kamil and Making a Stream(List dfs gcd)
  9. META-DATASET 数据集类别划分(ILSVRS2012)
  10. X线DR医学图像 --- DR医用滤线栅及摩尔纹详解 (一) 滤线栅的原理