700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux驱动开发16 网络设备驱动框架

Linux驱动开发16 网络设备驱动框架

时间:2022-11-14 04:40:06

相关推荐

Linux驱动开发16 网络设备驱动框架

嵌入式下的网络硬件接口

首先,嵌入式网络硬件分为两部分:MAC 和 PHY,大家都是通过看数据手册来判断一款 SOC 是否支持网络,如果一款芯片数据手册说自己支持网络,一般都是说的这款 SOC 内置 MAC,MAC 类似 I2C 控制器、SPI 控制器一样的外设。但是光有 MAC还不能直接驱动网络,还需要另外一个芯片:PHY,因此对于内置 MAC 的 SOC,其外部必须搭配一个 PHY 芯片。 如果不内置MAC 则SOC内部没有网络MAC外设没有内部 MAC,那么可以找个外置的 MAC 芯片啊,不过一般这种外置的网络芯片都是MAC+PHY 一体的。比如三星 linux 开发板里面用的最多的DM9000,因为三星的芯片基本没有内部 MAC(比如 S3C2440、S5PV210,4412 等),所以三星的开发板都是通过外置的 DM9000 来完成有线网络功能W5500。这个一般用于单片机领域,单片机通过 SPI 接口与 W5500 进行通信,由于W5500 内置了硬件 TCP/IP 协议栈,因此单片机就不需要移植负责的软件协议栈外置MAC芯片的缺点就是网络效率不高,因为一般芯片内置的 MAC 会有网络加速引擎,比如网络专用 DMA,网络处理效率会很高。而且此类芯片网速都不快,基本就是10/100MSOC内部集成网络MAC外设一般常见的通用 SOC 都会集成网络 MAC 外设,比如 STM32F4/F7/H7 系列、NXP 的 I.MX 系列,内部集成网络 MAC 的优点如下: ①、内部 MAC 外设会有专用的加速模块,比如专用的 DMA,加速网速数据的处理。 ②、网速快,可以支持 10/100/1000M 网速。 ③、外接 PHY 可选择性多,成本低。 内部的 MAC 外设会通过MII 或者 RMII 接口来连接外部的 PHY 芯片,MII/RMII 接口用来传输网络数据。另外主控需要配置或读取 PHY 芯片,也就是读写 PHY 的内部寄存器,所以还需要一个控制接口,叫做 MIDO,MDIO 很类似 IIC,也是两根线,一根数据线叫做 MDIO,一根时钟线叫做 MDC。 I.MX6ULL 就有两个 10M/100M 的网络 MAC 外设,正点原子 ALPHA 开发板板载了两颗 PHY 芯片,型号为 LAN8720。

MII/RMII接口

MII接口MII 全称是 Media Independent Interface,直译过来就是介质独立接口,它是 IEEE-802.3 定 义的以太网标准接口,MII 接口用于以太网 MAC 连接 PHY 芯片,连接示意图如图 69.1.2.1 所示RMII接口RMII 全称是 Reduced Media Independent Interface,翻译过来就是精简的介质独立接口,也 就是 MII 接口的精简版本。RMII 接口只需要 7 根数据线,相比 MII 直接减少了 9 根,极大的方便了板子布线,RMII 接口连接 PHY 芯片的示意图如图 69.1.2.2 所示:

MDIO接口

MDIO 全称是 Management Data Input/Output,直译过来就是管理数据输入输出接口,是一 个简单的两线串行接口一根 MDIO 数据线,一根 MDC 时钟线。驱动程序可以通过 MDIO 和 MDC 这两根线访问 PHY 芯片的任意一个寄存器。MDIO 接口支持多达 32 个 PHY。同一时刻内只能对一个 PHY 进行操作,那么如何区分这 32 个 PHY 芯片呢?和 IIC 一样,使用器件地址即可。同一 MDIO 接口下的所有 PHY 芯片,其器件地址不能冲突,必须保证唯一,具体器件地址值要查阅相应的 PHY 数据手册。因此,MAC 和外部 PHY 芯片进行连接的时候主要是 MII/RMII 和 MDIO 接口,另外可能还需要复位、中断等其他引脚。

RJ45接口

网络设备是通过网线连接起来的,插入网线的叫做 RJ45 座 RJ45 座子上一般有两个灯,一个黄色(橙色),一个绿色,绿色亮的话表示网络连接正常,黄色闪烁的话说明当前正在进行网络通信。这两个灯由 PHY 芯片控制,PHY 芯片会有两个引脚来连接 RJ45 座上的这两个灯。内部 MAC+外部 PHY+RJ45 座(内置网络变压器)就组成了一个完整的嵌入式网络接口硬件

PHY芯片详解

PHY 是 IEEE 802.3 规定的一个标准模块,前面说了,SOC 可以对 PHY 进行配置或者读取 PHY 相关状态,这个就需要 PHY 内部寄存器去实现了。PHY 芯片寄存器地址空间为 5 位,地址 0~31 共 32 个寄存器,IEEE 定义了 0~15 这 16 个寄存器的功能,16~31 这 16 个寄存器由厂商自行实现。也就是说不管你用的哪个厂家的 PHY 芯片,其中 0~15 这 16 个寄存器是一模一样的。仅靠这 16 个寄存器是完全可以驱动起 PHY 芯片的,至少能保证基本的网络数据通信,因此 Linux 内核有通用 PHY 驱动

PHY的前16个寄存器详解

Linux内核网络驱动框架

net_device 结构体(注册与注销)

Linux 内核使用 net_device 结构体表示一个具体的网络设备,net_device 结构体定义在 include/linux/netdevice.h 中,net_device 是一个庞大的结构体

struct net_device {charname[IFNAMSIZ];struct hlist_nodename_hlist;char *ifalias;/**I/O specific fields*FIXME: Merge these and struct ifmap into one*/unsigned longmem_end;unsigned longmem_start;unsigned longbase_addr;intirq;atomic_tcarrier_changes;/**Some hardware also needs these fields (state,dev_list,*napi_list,unreg_list,close_list) but they are not*part of the usual set specified in Space.c.*/unsigned longstate;struct list_headdev_list;struct list_headnapi_list;struct list_headunreg_list;struct list_headclose_list;struct list_headptype_all;struct list_headptype_specific;struct {struct list_head upper;struct list_head lower;} adj_list;struct {struct list_head upper;struct list_head lower;} all_adj_list;netdev_features_tfeatures;netdev_features_thw_features;netdev_features_twanted_features;netdev_features_tvlan_features;netdev_features_thw_enc_features;netdev_features_tmpls_features;intifindex;intgroup;struct net_device_statsstats;atomic_long_trx_dropped;atomic_long_ttx_dropped;#ifdef CONFIG_WIRELESS_EXTconst struct iw_handler_def *wireless_handlers;struct iw_public_data *wireless_data;#endifconst struct net_device_ops *netdev_ops;const struct ethtool_ops *ethtool_ops;#ifdef CONFIG_NET_SWITCHDEVconst struct swdev_ops *swdev_ops;#endifconst struct header_ops *header_ops;unsigned intflags;unsigned intpriv_flags;unsigned shortgflags;unsigned shortpadded;unsigned charoperstate;unsigned charlink_mode;unsigned charif_port;unsigned chardma;unsigned intmtu;unsigned shorttype;unsigned shorthard_header_len;unsigned shortneeded_headroom;unsigned shortneeded_tailroom;/* Interface address info. */unsigned charperm_addr[MAX_ADDR_LEN];unsigned charaddr_assign_type;unsigned charaddr_len;unsigned shortneigh_priv_len;unsigned shortdev_id;unsigned shortdev_port;spinlock_taddr_list_lock;unsigned charname_assign_type;booluc_promisc;struct netdev_hw_addr_listuc;struct netdev_hw_addr_listmc;struct netdev_hw_addr_listdev_addrs;#ifdef CONFIG_SYSFSstruct kset*queues_kset;#endifunsigned intpromiscuity;unsigned intallmulti;/* Protocol specific pointers */#if IS_ENABLED(CONFIG_VLAN_8021Q)struct vlan_info __rcu*vlan_info;#endif#if IS_ENABLED(CONFIG_NET_DSA)struct dsa_switch_tree*dsa_ptr;#endif#if IS_ENABLED(CONFIG_TIPC)struct tipc_bearer __rcu *tipc_ptr;#endifvoid *atalk_ptr;struct in_device __rcu*ip_ptr;struct dn_dev __rcu*dn_ptr;struct inet6_dev __rcu*ip6_ptr;void*ax25_ptr;struct wireless_dev*ieee80211_ptr;struct wpan_dev*ieee802154_ptr;#if IS_ENABLED(CONFIG_MPLS_ROUTING)struct mpls_dev __rcu*mpls_ptr;#endif/** Cache lines mostly used on receive path (including eth_type_trans())*/unsigned longlast_rx;/* Interface address info used in eth_type_trans() */unsigned char*dev_addr;#ifdef CONFIG_SYSFSstruct netdev_rx_queue*_rx;unsigned intnum_rx_queues;unsigned intreal_num_rx_queues;#endifunsigned longgro_flush_timeout;rx_handler_func_t __rcu*rx_handler;void __rcu*rx_handler_data;struct netdev_queue __rcu *ingress_queue;unsigned charbroadcast[MAX_ADDR_LEN];#ifdef CONFIG_RFS_ACCELstruct cpu_rmap*rx_cpu_rmap;#endifstruct hlist_nodeindex_hlist;/** Cache lines mostly used on transmit path*/struct netdev_queue*_tx ____cacheline_aligned_in_smp;unsigned intnum_tx_queues;unsigned intreal_num_tx_queues;struct Qdisc*qdisc;unsigned longtx_queue_len;spinlock_ttx_global_lock;intwatchdog_timeo;#ifdef CONFIG_XPSstruct xps_dev_maps __rcu *xps_maps;#endif/* These may be needed for future network-power-down code. *//** trans_start here is expensive for high speed devices on SMP,* please use netdev_queue->trans_start instead.*/unsigned longtrans_start;struct timer_listwatchdog_timer;int __percpu*pcpu_refcnt;struct list_headtodo_list;struct list_headlink_watch_list;enum { NETREG_UNINITIALIZED=0,NETREG_REGISTERED,/* completed register_netdevice */NETREG_UNREGISTERING,/* called unregister_netdevice */NETREG_UNREGISTERED,/* completed unregister todo */NETREG_RELEASED,/* called free_netdev */NETREG_DUMMY,/* dummy device for NAPI poll */} reg_state:8;bool dismantle;enum {RTNL_LINK_INITIALIZED,RTNL_LINK_INITIALIZING,} rtnl_link_state:16;void (*destructor)(struct net_device *dev);#ifdef CONFIG_NETPOLLstruct netpoll_info __rcu*npinfo;#endifpossible_net_tnd_net;/* mid-layer private */union {void*ml_priv;struct pcpu_lstats __percpu*lstats;struct pcpu_sw_netstats __percpu*tstats;struct pcpu_dstats __percpu*dstats;struct pcpu_vstats __percpu*vstats;};struct garp_port __rcu*garp_port;struct mrp_port __rcu*mrp_port;struct devicedev;const struct attribute_group *sysfs_groups[4];const struct attribute_group *sysfs_rx_queue_group;const struct rtnl_link_ops *rtnl_link_ops;/* for setting kernel sock attribute on TCP connection setup */#define GSO_MAX_SIZE65536unsigned intgso_max_size;#define GSO_MAX_SEGS65535u16gso_max_segs;u16gso_min_segs;#ifdef CONFIG_DCBconst struct dcbnl_rtnl_ops *dcbnl_ops;#endifu8 num_tc;struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE];u8 prio_tc_map[TC_BITMASK + 1];#if IS_ENABLED(CONFIG_FCOE)unsigned intfcoe_ddp_xid;#endif#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)struct netprio_map __rcu *priomap;#endifstruct phy_device *phydev;struct lock_class_key *qdisc_tx_busylock;};

1、申请net_device编写网络驱动的时候首先要申请 net_device,使用 alloc_netdev 函数来申请 net_device,这 是一个宏,宏定义如下2、删除net_device当我们注销网络驱动的时候需要释放掉前面已经申请到的 net_device,释放函数为free_netdev,函数原型如下: void free_netdev(struct net_device *dev)3、注册net_devicenet_device 申请并初始化完成以后就需要向内核注册 net_device,要用到函数 register_netdev, 函数原型如下: int register_netdev(struct net_device *dev)4、注销net_device既然有注册,那么必然有注销,注销 net_device 使用函数 unregister_netdev,函数原型如下: void unregister_netdev(struct net_device *dev)

net_device_ops结构体(网络设备操作集)

net_device 有个非常重要的成员变量:netdev_ops,为 net_device_ops 结构体指针类型,这就是网络设备的操作集。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);int(*ndo_open)(struct net_device *dev);int(*ndo_stop)(struct net_device *dev);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 *accel_priv,select_queue_fallback_t fallback);void(*ndo_change_rx_flags)(struct net_device *dev,int flags);void(*ndo_set_rx_mode)(struct net_device *dev);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);int(*ndo_set_config)(struct net_device *dev,struct ifmap *map);int(*ndo_change_mtu)(struct net_device *dev,int new_mtu);int(*ndo_neigh_setup)(struct net_device *dev,struct neigh_parms *);void(*ndo_tx_timeout) (struct net_device *dev);struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev,struct rtnl_link_stats64 *storage);struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);int(*ndo_vlan_rx_add_vid)(struct net_device *dev,__be16 proto, u16 vid);int(*ndo_vlan_rx_kill_vid)(struct net_device *dev,__be16 proto, u16 vid);#ifdef CONFIG_NET_POLL_CONTROLLERvoid(*ndo_poll_controller)(struct net_device *dev);int(*ndo_netpoll_setup)(struct net_device *dev,struct netpoll_info *info);void(*ndo_netpoll_cleanup)(struct net_device *dev);#endif#ifdef CONFIG_NET_RX_BUSY_POLLint(*ndo_busy_poll)(struct napi_struct *dev);#endifint(*ndo_set_vf_mac)(struct net_device *dev,int queue, u8 *mac);int(*ndo_set_vf_vlan)(struct net_device *dev,int queue, u16 vlan, u8 qos);int(*ndo_set_vf_rate)(struct net_device *dev,int vf, int min_tx_rate,int max_tx_rate);int(*ndo_set_vf_spoofchk)(struct net_device *dev,int vf, bool setting);int(*ndo_get_vf_config)(struct net_device *dev,int vf,struct ifla_vf_info *ivf);int(*ndo_set_vf_link_state)(struct net_device *dev,int vf, int link_state);int(*ndo_set_vf_port)(struct net_device *dev,int vf,struct nlattr *port[]);int(*ndo_get_vf_port)(struct net_device *dev,int vf, struct sk_buff *skb);int(*ndo_set_vf_rss_query_en)(struct net_device *dev,int vf, bool setting);int(*ndo_setup_tc)(struct net_device *dev, u8 tc);#if IS_ENABLED(CONFIG_FCOE)int(*ndo_fcoe_enable)(struct net_device *dev);int(*ndo_fcoe_disable)(struct net_device *dev);int(*ndo_fcoe_ddp_setup)(struct net_device *dev,u16 xid,struct scatterlist *sgl,unsigned int sgc);int(*ndo_fcoe_ddp_done)(struct net_device *dev,u16 xid);int(*ndo_fcoe_ddp_target)(struct net_device *dev,u16 xid,struct scatterlist *sgl,unsigned int sgc);int(*ndo_fcoe_get_hbainfo)(struct net_device *dev,struct netdev_fcoe_hbainfo *hbainfo);#endif#if IS_ENABLED(CONFIG_LIBFCOE)#define NETDEV_FCOE_WWNN 0#define NETDEV_FCOE_WWPN 1int(*ndo_fcoe_get_wwn)(struct net_device *dev,u64 *wwn, int type);#endif#ifdef CONFIG_RFS_ACCELint(*ndo_rx_flow_steer)(struct net_device *dev,const struct sk_buff *skb,u16 rxq_index,u32 flow_id);#endifint(*ndo_add_slave)(struct net_device *dev,struct net_device *slave_dev);int(*ndo_del_slave)(struct net_device *dev,struct net_device *slave_dev);netdev_features_t(*ndo_fix_features)(struct net_device *dev,netdev_features_t features);int(*ndo_set_features)(struct net_device *dev,netdev_features_t features);int(*ndo_neigh_construct)(struct neighbour *n);void(*ndo_neigh_destroy)(struct neighbour *n);int(*ndo_fdb_add)(struct ndmsg *ndm,struct nlattr *tb[],struct net_device *dev,const unsigned char *addr,u16 vid,u16 flags);int(*ndo_fdb_del)(struct ndmsg *ndm,struct nlattr *tb[],struct net_device *dev,const unsigned char *addr,u16 vid);int(*ndo_fdb_dump)(struct sk_buff *skb,struct netlink_callback *cb,struct net_device *dev,struct net_device *filter_dev,int idx);int(*ndo_bridge_setlink)(struct net_device *dev,struct nlmsghdr *nlh,u16 flags);int(*ndo_bridge_getlink)(struct sk_buff *skb,u32 pid, u32 seq,struct net_device *dev,u32 filter_mask,int nlflags);int(*ndo_bridge_dellink)(struct net_device *dev,struct nlmsghdr *nlh,u16 flags);int(*ndo_change_carrier)(struct net_device *dev,bool new_carrier);int(*ndo_get_phys_port_id)(struct net_device *dev,struct netdev_phys_item_id *ppid);int(*ndo_get_phys_port_name)(struct net_device *dev,char *name, size_t len);void(*ndo_add_vxlan_port)(struct net_device *dev,sa_family_t sa_family,__be16 port);void(*ndo_del_vxlan_port)(struct net_device *dev,sa_family_t sa_family,__be16 port);void*(*ndo_dfwd_add_station)(struct net_device *pdev,struct net_device *dev);void(*ndo_dfwd_del_station)(struct net_device *pdev,void *priv);netdev_tx_t(*ndo_dfwd_start_xmit) (struct sk_buff *skb,struct net_device *dev,void *priv);int(*ndo_get_lock_subclass)(struct net_device *dev);netdev_features_t(*ndo_features_check) (struct sk_buff *skb,struct net_device *dev,netdev_features_t features);int(*ndo_set_tx_maxrate)(struct net_device *dev,int queue_index,u32 maxrate);int(*ndo_get_iflink)(const struct net_device *dev);};

第 2 行:ndo_init 函数,当第一次注册网络设备的时候此函数会执行,设备可以在此函数中做一些需要退后初始化的内容,不过一般驱动中不使用此函数,虚拟网络设备可能会使用。 第 3 行:ndo_uninit 函数,卸载网络设备的时候此函数会执行。 第 4 行:ndo_open 函数,打开网络设备的时候此函数会执行,网络驱动程序需要实现此函数,非常重要!以 NXP 的 I.MX 系列 SOC 网络驱动为例,会在此函数中做如下工作: ·使能网络外设时钟。 ·申请网络所使用的环形缓冲区。 ·初始化 MAC 外设。 ·绑定接口对应的 PHY。 ·如果使用 NAPI 的话要使能 NAPI 模块,通过 napi_enable 函数来使能。 ·开启 PHY。 ·调用 netif_tx_start_all_queues 来使能传输队列,也可能调用 netif_start_queue 函数。 ·…… 第 5 行:ndo_stop 函数,关闭网络设备的时候此函数会执行,网络驱动程序也需要实现此函数。以 NXP 的 I.MX 系列 SOC 网络驱动为例,会在此函数中做如下工作: ·停止 PHY。 ·停止 NAPI 功能。 ·停止发送功能。 ·关闭 MAC。 ·断开 PHY 连接。 ·关闭网络时钟。 ·释放数据缓冲区。 ·…… 第 6 行:ndo_start_xmit 函数,当需要发送数据的时候此函数就会执行,此函数有一个参数为 sk_buff 结构体指针,sk_buff 结构体在 Linux 的网络驱动中非常重要,sk_buff 保存了上层传递给网络驱动层的数据。也就是说,要发送出去的数据都存在了 sk_buff 中,关于 sk_buff 稍后会做详细的讲解。如果发送成功的话此函数返回 NETDEV_TX_OK,如果发送失败了就返回NETDEV_TX_BUSY,如果发送失败了我们就需要停止队列。 第 8 行:ndo_select_queue 函数,当设备支持多传输队列的时候选择使用哪个队列 第 14 行:ndo_set_rx_mode 函数,此函数用于改变地址过滤列表,根据 net_device 的 flags 成员变量来设置 SOC 的网络外设寄存器。比如 flags 可能为 IFF_PROMISC、IFF_ALLMULTI 或 IFF_MULTICAST,分别表示混杂模式、单播模式或多播模式。 第 15 行:ndo_set_mac_address 函数,此函数用于修改网卡的 MAC 地址,设置 net_device的 dev_addr 成员变量,并且将 MAC 地址写入到网络外设的硬件寄存器中。 第 17 行:ndo_validate_addr 函数,验证 MAC 地址是否合法,也即是验证 net_device 的 dev_addr 中的 MAC 地址是否合法,直接调用 is_valid_ether_addr 函数。 第 18 行:ndo_do_ioctl 函数,用户程序调用 ioctl 的时候此函数就会执行,比如 PHY 芯片相关的命令操作,一般会直接调用 phy_mii_ioctl 函数。第 22 行:ndo_change_mtu 函数,更改 MTU 大小。 第 26 行:ndo_tx_timeout 函数,当发送超时的时候产生会执行,一般都是网络出问题了导致发送超时。一般可能会重启 MAC 和 PHY,重新开始数据发送等。 第 37 行:ndo_poll_controller 函数,使用查询方式来处理网卡数据的收发。 第 104 行:ndo_set_features 函数,修改 net_device 的 features 属性,设置相应的硬件属性。

sk_buff结构体(管理数据包)

我们重点来看一下 sk_buff 这个结构体,sk_buff 是 Linux 网络重要的数据结构,用于管理 接收或发送数据包,sk_buff 结构体定义在 include/linux/skbuff.h 中 网络是分层的,对于应用层而言不用关系具体的底层是如何工作的,只需要按照协议将要发送或接收的数据打包好即可。打包好以后都通过dev_queue_xmit 函数将数据发送出去,接收数据的话使用netif_rx 函数即可,我们依次来看一下这两个函数1dev_queue_xmit函数此函数用于将网络数据发送出去,函数定义在 include/linux/netdevice.h 中,函数原型如下: static inline int dev_queue_xmit(struct sk_buff *skb)2netif_rx函数上层接收数据的话使用 netif_rx 函数,但是最原始的网络数据一般是通过轮询、中断或 NAPI 的方式来接收。netif_rx 函数定义在 net/core/dev.c 中,函数原型如下: int netif_rx(struct sk_buff *skb)

针对 sk_buff 内核提供了一系列的操作与管理函数,我们简单看一些常见的 API 函数:

1、分配sk_buff要使用 sk_buff 必须先分配,首先来看一下 alloc_skb 这个函数,此函数定义在 include/linux/skbuff.h 中,函数原型如下: static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)在网络设备驱动中常常使用 netdev_alloc_skb 来为某个设备申请一个用于接收的 skb_buff, 此函数也定义在 include/linux/skbuff.h 中,函数原型如下:(用这个)static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, unsigned int length)2、释放sk_buff当使用完成以后就要释放掉 sk_buff,释放函数可以使用 kfree_skb,函数定义在 include/linux/skbuff.c 中,函数原型如下: void kfree_skb(struct sk_buff *skb) 对于网络设备而言最好使用如下所示释放函数:(用这个)void dev_kfree_skb (struct sk_buff *skb) 函数只要一个参数 skb,就是要释放的 sk_buff。3skb_putskb_pushsbk_pullskb_reserve这四个函数用于变更 sk_buffskb_put 函数,此函数用于在尾部扩展 skb_buff 的数据区,也就将 skb_buff 的 tail 后移 n 个字节,从而导致 skb_buff 的 len 增加 n 个字节,原型如下: unsigned char *skb_put(struct sk_buff *skb, unsigned int len) skb_push 函数用于在头部扩展 skb_buff 的数据区,函数原型如下所示: unsigned char *skb_push(struct sk_buff *skb, unsigned int len) sbk_pull 函数用于从sk_buff 的数据区起始位置删除数据,函数原型如下所示: unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) sbk_reserve 函数用于调整缓冲区的头部大小,方法很简单讲 skb_buff 的 data 和 tail 同时后 移 n 个字节即可,函数原型如下所示: static inline void skb_reserve(struct sk_buff *skb, int len)

网络NAPI 处理机制 与napi_struct 实例

Linux 里面的网络数据接收也轮询和中断两种, 1)中断的好处就是响应快,数据量小的时候处理及时,速度快,但是一旦当数据量大,而且都是短帧的时候会导致中断频繁发生,消耗大量的 CPU 处理时间在中断自身处理上。 2)轮询恰好相反,响应没有中断及时,但是在处理大量数据的时候不需要消耗过多的 CPU 处理时间 Linux 在这两个处理方式的基础上提出了另外一种网络数据接收的处理方法:NAPI(New API),NAPI 是一种高效的网络处理技术。 NAPI 的核心思想就是不全部采用中断来读取网络数据,而是采用中断来唤醒数据接收服务程序在接收服务程序中采用 POLL 的方法来轮询处理数据。这种方法的好处就是可以提高短数据包的接收效率,减少中断处理的时间。 讲解一下如何在驱动中使用 NAPI, Linux 内核使用结构体 napi_struct 表示 NAPI,在使用 NAPI 之前要先初始化一个napi_struct 实例1、初始化NAPI首先要初始化一个 napi_struct 实例,使用netif_napi_add 函数,此函数定义在 net/core/dev.c中,函数原型如下: void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight)2、删除NAPI如果要删除 NAPI,使用 netif_napi_del 函数即可,函数原型如下: void netif_napi_del(struct napi_struct *napi)3、使能NAPI初始化完 NAPI 以后,必须使能才能使用,使用函数 napi_enable,函数原型如下: inline void napi_enable(struct napi_struct *n)4、关闭NAPI关闭 NAPI 使用 napi_disable 函数即可,函数原型如下: void napi_disable(struct napi_struct *n)5、检查NAPI是否可以进行调度使用 napi_schedule_prep 函数检查 NAPI 是否可以进行调度,函数原型如下: inline bool napi_schedule_prep(struct napi_struct *n)6NAPI调度如果可以调度的话就进行调度,使用__napi_schedule 函数完成 NAPI 调度,函数原型如下: void __napi_schedule(struct napi_struct *n)7NAPI处理完成NAPI 处理完成以后需要调用 napi_complete 函数来标记 NAPI 处理完成,函数原型如下: inline void napi_complete(struct napi_struct *n)

实验

不需要写代码 真正如果需要写网络驱动框架去看懂NXP官方的即可

首 先 肯 定 是 设 备 树 , NXP 的 I.MX 系 列 SOC 网 络 绑 定 文 档 为 Documentation/devicetree/bindings/net/fsl-fec.txt,此绑定文档描述了 I.MX 系列 SOC 网络设备树 节点的要求。 打开 imx6ull.dtsi,找到如下 I.MX6ULL 的两个网络外设节点 示例代码 69.4.1.1 是 NXP 官方编写的,我们不需要去修改,但是示例代码 69.4.1.1 是不能工作的,还需要根据实际情况添加或修改一些属性。打开 imx6ull-alientek-emmc.dts,找到如下内容: 首先来看一下 I.MX6ULL 的网络控制器部分驱动,从示例代码 69.4.1.1 中可以看出, compatible 属性有两个值“fsl,imx6ul-fec”和“fsl,imx6q-fec”,通过在 linux 内核源码中搜索这两个字符串即可找到对应的驱动文件,驱动文件为 drivers/net/ethernet/freescale/fec_main.c,打开fec_main.c,找到如下所示内容:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。