Linux学习第51天:Linux网络驱动实验(一):一往(网)情深

Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


以下为本节的思维导图:

一、嵌入式网络简介

1.嵌入式下的网络硬件接口

        MAC和PHY

        强烈建议大家选择内部带有网络 MAC 外设的主控 SOC!
 

1)SOC 内部没有网络 MAC 外设
 

        内部没有MAC的SOC如何上网?外置MAC芯片。

                DM9000 对 SOC 提供了一个 SRAM 接口, SOC 会以 SRAM 的方式操作 DM9000。

                W5500 SPI接口 内部甚至集成了硬件 TCP/IP 协议栈


2)SOC 内部集成网络 MAC 外设

2.MII/RMII接口    MAC---MII/RMII---PHY

        MII信号线太多,使用的较少。

        发送和接收公用时钟。

3.MDIO接口    读写PHY内部寄存器

        一根 MDIO 数据线,一根 MDC 时钟线。驱动程序可以通过 MDIO 和
MDC 这两根线访问 PHY 芯片的任意一个寄存器。 MDIO 接口支持多达 32 个 PHY。同一时刻
内只能对一个 PHY 进行操作。


4.RJ45接口

        RJ45 座要与 PHY 芯片连接在一起,但是中间需要一个网络变压器,网络变压器用于隔离
以及滤波等,网络变压器也是一个芯片。

        很多 RJ45 座子内部已经集成了网络变压器。RJ45 座子上一般有两个灯,一个黄色(橙色),一个绿色,绿色亮的话表示网络连接正常黄色闪烁的话说明当前正在进行网络通信。

5.I.MX6ULL ENET接口简介

二、PHY芯片详解

1.PHY基础知识简介

        Linux 内核有通用 PHY 驱动,
 

2.LAN8720A详解

        支持非 IEEE 802.3 规范的中断功能。

        ALPHA 开发板是通过 I.MX6ULL 的ENET1_REF_CLK 和 ENET2_REF_CLK 这两个网络时钟引脚来为 LAN8720A 提供 50MHz 的时钟。

3.SR8201F详解

三、Linux内核网络驱动框架

1.net_device结构体

        net_device 结构体表示一个具体的网络设备。        

        网络驱动的核心就是初始化 net_device 结构体中的各个成员变量,然后将初始化完成以后的 net_device 注册到 Linux 内核中。

char name[IFNAMSIZ];//定义网络设备的名称
unsigned long mem_end;//共享内存的结束地址
unsigned long mem_start;//共享内存的起始地址
unsigned long base_addr;//网络设备的IO地址
int irq;//网络设备的中断号
struct list_head dev_list;//全局网络设备列表
struct list_head napi_list;//napi 网络设备的列表入口
struct list_head unreg_list;//注销(unregister)的网络设备列表入口
struct list_head close_list;//关闭的网络设备列表入口
const struct net_device_ops *netdev_ops;//网络设备操作函数集
const struct ethtool_ops *ethtool_ops;//网络管理工具相关函数集
const struct header_ops *header_ops;//头部的相关操作函数集,比如创建、解析、缓冲等。

unsigned int flags;//网络接口标识
unsigned char if_port;//指定接口的端口类型
unsigned char dma;//网络设备所使用的 DMA 通道,不是所有的设备都会用到 DMA。

unsigned int mtu;//网络最大传输单元,为 1500。
unsigned short type;//用于指定 ARP 模块的类型
unsigned char perm_addr[MAX_ADDR_LEN];//网卡的硬件地址
unsigned long last_rx;//最后接收的数据包时间戳,记录的是 jiffies。
unsigned char *dev_addr;//dev_addr 也是硬件地址,是当前分配的 MAC 地址,可以通过软件修改。
struct netdev_rx_queue *_rx;//接收队列

unsigned int num_rx_queues;//num_rx_queues 是接收队列数量
unsigned int real_num_rx_queues;//当前活动的队列数量
161 struct netdev_queue *_tx ____cacheline_aligned_in_smp;//发送队列
162 unsigned int num_tx_queues;//发送队列数量
163 unsigned int real_num_tx_queues;//当前有效的发送队列数量
unsigned long trans_start;//最后的数据包发送的时间戳
struct phy_device *phydev;// PHY 设备

        1)、申请 net_device

        使用 alloc_netdev 函数来申请 net_device。

1 #define alloc_netdev(sizeof_priv, name, name_assign_type, setup) 
2 alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)
struct net_device * alloc_netdev_mqs ( int sizeof_priv,
const char *name,
void (*setup) (struct net_device *))// 回调函数,初始化设备的设备后调用此函数。
unsigned int txqs,//分配的发送队列数量。
unsigned int rxqs);//分配的接收队列数量。


//返回值: 如果申请成功的话就返回申请到的 net_device 指针,失败的话就返回 NULL。

        2)、删除 net_device

void free_netdev(struct net_device *dev)

        3)、注册 net_device

int register_netdev(struct net_device *dev)

        4)、注销 net_device

void unregister_netdev(struct net_device *dev)

2.net_device_ops结构体

        ndo_open 函数,打开网络设备的时候此函数会执行,网络驱动程序需要实现此函
数,非常重要!

        要发送出去的数据都存在了 sk_buff 中
        ndo_uninit 函数,卸载网络设备的时候此函数会执行。
        ndo_select_queue 函数,当设备支持多传输队列的时候选择使用哪个队列。
        ndo_set_rx_mode 函数,此函数用于改变地址过滤列表
        ndo_set_mac_address 函数,此函数用于修改网卡的 MAC 地址
        ndo_validate_addr 函数,验证 MAC 地址是否合法
        ndo_do_ioctl 函数,用户程序调用 ioctl 的时候此函数就会执行,比如 PHY 芯片相关的命令操作,一般会直接调用 phy_mii_ioctl 函数。
        ndo_change_mtu 函数,更改 MTU 大小。
        ndo_tx_timeout 函数,当发送超时的时候函数会执行,一般都是网络出问题了导致发送超时。一般可能会重启 MAC 和 PHY,重新开始数据发送等。
        ndo_poll_controller 函数,使用查询方式来处理网卡数据的收发。
        ndo_set_features 函数,修改 net_device 的 features 属性,设置相应的硬件属性。


3.sk_buff结构体

        打包好以后都通过 dev_queue_xmit 函数将数据发送出去,接收数据的话使用 netif_rx 函数即可.

1)、dev_queue_xmit 函数

static inline int dev_queue_xmit(struct sk_buff *skb)//将网络数据发送出去。


2)、netif_rx 函数

        上层接收数据的话,使用netif_rx函数。

int netif_rx(struct sk_buff *skb)

sk_buff 是 Linux 网络重要的数据结构,用于管理接收或发送数据包。

(1)、分配sk_buff

static inline struct sk_buff *alloc_skb(unsigned int size,
gfp_t priority)

        使用 netdev_alloc_skb 来为某个设备申请一个用于接收的 skb_buff。

(2)、释放 sk_buff

void kfree_skb(struct sk_buff *skb)

        对于网络设备而言,最好使用下列函数释放函数:

void dev_kfree_skb (struct sk_buff *skb)

 (3)、skb_put、 skb_push、 sbk_pull 和 skb_reserve

        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)

4.网络NAPI处理机制

NAPI(New API), NAPI 是一种高效的网络处理技术。NAPI 的核心思想就是不全部采用中断来读取网络数据,而是采用中断来唤醒数据接收服务程序,在接收服务程序中采用 POLL 的方法来轮询处理数据。这种方法的好处就是可以提高短数据包的接收效率,减少中断处理的时间。
1)、初始化 NAPI

void netif_napi_add(struct net_device *dev,
struct napi_struct *napi,
int (*poll)(struct napi_struct *, int),
int weight)

2)、删除 NAPI

void netif_napi_del(struct napi_struct *napi)

3)、使能 NAPI

inline void napi_enable(struct napi_struct *n)

4)、关闭 NAPI

void napi_disable(struct napi_struct *n)

5)、检查 NAPI 是否可以进行调度

inline bool napi_schedule_prep(struct napi_struct *n)

6)、NAPI 调度

void __napi_schedule(struct napi_struct *n)

7)、NAPI 处理完成

inline void napi_complete(struct napi_struct *n)

以下内容将在下一笔记中总结:


四、I.MX6ULL网络驱动简介

1.I.MX6ULL网络外设设备树

2.I.MX6ULL网络驱动源码简析

3.fec_netdev_ops操作集

4.Linux内核PHY子系统与MDIO总线简析

五、网络驱动实验测试

1.LAN8720 PHY驱动测试

2.通用PHY驱动测试

3.DHCP功能配置

六、单网卡使用

1.只使用ENET2网卡

2.只使用ENET1网卡


本笔记为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值