嵌入式下的网络硬件接口

        首先,嵌入式网络硬件分为两部分: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/100M
SOC 内部集成网络 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 {char          name[IFNAMSIZ];struct hlist_node    name_hlist;char             *ifalias;/**    I/O specific fields*    FIXME: Merge these and struct ifmap into one*/unsigned long     mem_end;unsigned long       mem_start;unsigned long     base_addr;int           irq;atomic_t        carrier_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 long      state;struct list_head  dev_list;struct list_head   napi_list;struct list_head  unreg_list;struct list_head close_list;struct list_head ptype_all;struct list_head  ptype_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_t  features;netdev_features_t  hw_features;netdev_features_t   wanted_features;netdev_features_t   vlan_features;netdev_features_t hw_enc_features;netdev_features_t   mpls_features;int           ifindex;int         group;struct net_device_stats   stats;atomic_long_t     rx_dropped;atomic_long_t        tx_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 int      flags;unsigned int      priv_flags;unsigned short       gflags;unsigned short       padded;unsigned char        operstate;unsigned char     link_mode;unsigned char     if_port;unsigned char       dma;unsigned int        mtu;unsigned short      type;unsigned short     hard_header_len;unsigned short      needed_headroom;unsigned short      needed_tailroom;/* Interface address info. */unsigned char      perm_addr[MAX_ADDR_LEN];unsigned char       addr_assign_type;unsigned char      addr_len;unsigned short     neigh_priv_len;unsigned short          dev_id;unsigned short          dev_port;spinlock_t       addr_list_lock;unsigned char        name_assign_type;bool           uc_promisc;struct netdev_hw_addr_list   uc;struct netdev_hw_addr_list   mc;struct netdev_hw_addr_list   dev_addrs;#ifdef CONFIG_SYSFSstruct kset        *queues_kset;
#endifunsigned int      promiscuity;unsigned int        allmulti;/* 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 long       last_rx;/* Interface address info used in eth_type_trans() */unsigned char      *dev_addr;#ifdef CONFIG_SYSFSstruct netdev_rx_queue *_rx;unsigned int       num_rx_queues;unsigned int      real_num_rx_queues;#endifunsigned long      gro_flush_timeout;rx_handler_func_t __rcu   *rx_handler;void __rcu      *rx_handler_data;struct netdev_queue __rcu *ingress_queue;unsigned char     broadcast[MAX_ADDR_LEN];
#ifdef CONFIG_RFS_ACCELstruct cpu_rmap      *rx_cpu_rmap;
#endifstruct hlist_node index_hlist;/** Cache lines mostly used on transmit path*/struct netdev_queue   *_tx ____cacheline_aligned_in_smp;unsigned int      num_tx_queues;unsigned int      real_num_tx_queues;struct Qdisc     *qdisc;unsigned long        tx_queue_len;spinlock_t     tx_global_lock;int          watchdog_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 long        trans_start;struct timer_list   watchdog_timer;int __percpu     *pcpu_refcnt;struct list_head   todo_list;struct list_head  link_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_t            nd_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 device dev;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_SIZE        65536unsigned int       gso_max_size;
#define GSO_MAX_SEGS        65535u16            gso_max_segs;u16            gso_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 int        fcoe_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_device
        net_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_pull skb_reserve 这四个函数用于变更 sk_buff
        skb_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,找到如下所示内容:

Linux驱动开发16 网络设备驱动框架相关推荐

  1. 浅谈 Linux 内核开发之网络设备驱动

    网络设备介绍 网络设备是计算机体系结构中必不可少的一部分,处理器如果想与外界通信,通常都会选择网络设备作为通信接口.众所周知,在 OSI(Open Systems Interconnection,开放 ...

  2. 浅谈 Linux 内核开发之网络设备驱动[转]

    本文来自:http://www.ibm.com/developerworks/cn/linux/l-cn-networkdriver/ 网络设备介绍 网络 设备是计算机体系结构中必不可少的一部分,处理 ...

  3. Linux驱动开发1:驱动开发与裸机开发的区别

    Linux驱动开发1:驱动开发与裸机开发的区别 1.裸机驱动开发回顾: 裸机驱动开发是非常底层的,跟寄存器打交道,有些MCU为了方便我们开发,提供了一些库,让我们通过调用API函数来间接的实现利用寄存 ...

  4. <Linux开发>驱动开发 -之-platform 驱动

    <Linux开发>驱动开发 -之-platform 驱动 交叉编译环境搭建: <Linux开发> linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下: < ...

  5. <Linux开发>--驱动开发-- 字符设备驱动(3) 过程详细记录

    <Linux开发>–驱动开发-- 字符设备驱动(3) 过程详细记录 驱动开发是建立再系统之上的,前面作者也记录了系统移植的过程记录,如果有兴趣,可进入博主的主页查看相关文章,这里就不添加链 ...

  6. Linux驱动开发—内核I2C驱动详解

    Linux驱动开发--内核I2C驱动 I2C驱动文件结构 I2C数据传输过程 i2c_transfer i2c_msg I2C通讯常用的接口函数(老版本) 快速读写接口函数:(连续读写) 常用的读操作 ...

  7. Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)

    文章目录 全系列传送门 1. 在/arch/arm/boot/dts/imx6q-pinfunc.h查找 2. 在设备树配置文件中添加设备节点定义以及其引脚定义 3. 修改设备树文件添加配置 4. d ...

  8. Linux 设备驱动开发 —— platform设备驱动应用实例解析

    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...

  9. STM32MP157驱动开发——platform设备驱动(中)

    STM32MP157驱动开发--platform设备驱动(中) 0.前言 一.platform设备模块--设备信息解析 二.platform驱动模块--加载设备 三.测试App 四.编译及运行 相关文 ...

最新文章

  1. 详解Class类文件的结构(下)
  2. 图形学 射线相交算法_计算机图形学中的阴极射线管(CRT)
  3. go var 一个整数_Go语言学习基础-值、变量、常量
  4. Mozilla 修复已遭利用的两个火狐浏览器 0day
  5. ECCV 2016 paper list
  6. mmap映射方式读写本地文件
  7. 计算机考研安大好考还是郑大好考,这5所“低调到隐形”的211大学,不仅分数低,还好考...
  8. jdk8 下载 安装 配置及idea配置jdk环境
  9. 下载歌曲的时候嫌麻烦?打造专属你的音乐下载器
  10. JavaIO流,万物皆文件
  11. 8种在JavaScript数组中查找指定元素的方法(用于开发中数据的处理)
  12. java reflection 作用,全方位解读Java反射(reflection)
  13. cocos2dx中的颜色混合
  14. php-pfm并发,php-pfm配置详解
  15. [固态硬盘协议 第2回] PCIE 相对 SATA 的 4 大优势详解
  16. 事务、事务提交、事务回滚
  17. 【安科瑞产品中心】ASJ系列智能电力继电器-安科瑞薛炯毅
  18. 电池-外部DC双电源供电设备电源自动切换电路分享(上)
  19. 【RPA之家转载】2021年影响会计人员的十大信息技术榜单出炉,RPA再次登榜
  20. 微软lumia固件服务器,微软Lumia 950 XL刷Win10 ARM项目已适配20H1更新

热门文章

  1. 宠物APP开发四大注意事项,让你避免踩坑
  2. 23天读懂23种设计模式:原型模式(创建型)
  3. Zookeeper客户端kazoo的watch流程详解
  4. Origin画三维图的步骤
  5. 采访JavaEye社区资深会员 预测IBM收购Sun影响
  6. 解决:ThinkPHP 下载项目提示“Call to undefined function captcha_src()”问题
  7. 蓝桥杯 历届试题 5. 错误票据
  8. 军用大数据 - Spark机器学习
  9. matlab差分法求解ppt,Matlab实现电磁场数值计算.ppt
  10. 陈华,毕业于北京大学计算机系,唱吧创始人,“唱吧”创始人陈华:如果绕过问题,你也许就会错失改变世界的机会...