Hi3520d 网卡驱动源码分析

来源:互联网 发布:新锐网络大学 编辑:程序博客网 时间:2024/04/28 18:35

                                                                 Hi3520d 网卡驱动源码分析

一、ioremap_nocache

跟体系结构有关

 

二、kzalloc 与kmalloc

kzalloc调用kmalloc,在此基础上申请内存出初始化0

GFP_KERNEL:可以休眠,函数可重入

GFP_ATOMIC:不可以休眠,一般放在终端上下文申请

 

三、网卡驱动Src: src/drivers/net/hieth-sf/net.c

1、驱动模块

module_init(hieth_init);

module_exit(hieth_exit);

2、驱动模块init

ret =platform_device_register(&hieth_platform_device);

ret =platform_driver_register(&hieth_platform_driver);

3、平台驱动数据

static struct platform_driver hieth_platform_driver= {

       .probe         = hieth_plat_driver_probe,(见第5小节)

       .remove         =hieth_plat_driver_remove,

       .suspend        =hieth_plat_driver_suspend,

       .resume         =hieth_plat_driver_resume,

       .driver         = {

                .owner  = THIS_MODULE,

                .name   = HIETH_DRIVER_NAME,

                .bus    = &platform_bus_type,

       },

};

4、平台设备数据

static struct platform_devicehieth_platform_device = {

       .name = HIETH_DRIVER_NAME,

       .id   = 0,

       .dev = {

                .platform_data  = NULL,

                .dma_mask = (u64 *)~0,

                .coherent_dma_mask = (u64)~0,

                .release =hieth_platform_device_release,

       },

       .num_resources = ARRAY_SIZE(hieth_resources),

       .resource = hieth_resources,

};

5、驱动探针hieth_plat_driver_probe

A. 网卡系统初始化: hieth_sys_init();

B.mdio bus初始化: hieth_mdiobus_driver_init();

C. 网卡私有probe: hieth_platdev_probe_port();(见第四节)

D. 注册网卡中断: request_irq(hieth_net_isr);(见第九节)

 

四、网卡私有probehieth_platdev_probe_port

1、申请网络设备

struct net_device *netdev = NULL;

struct hieth_netdev_local *ld;

netdev = alloc_etherdev(sizeof(*ld))

其中:alloc_etherdev  defined alloc_etherdev_mqs调用alloc_netdev_mqs

 

针对alloc_netdev_mqs函数:

(1)使用kzalloc申请网卡设备,总大小:sizeof(struct net_device) + sizeof_priv(= sizeof(hieth_netdev_loca))。也就是说网卡设备数据结构后面紧跟着私有网卡数据结构,后续使用通过net_priv(dev)获取私有数据

(2)硬件地址MAC,组播地址等初始化

dev_addr_init(),dev_mc_init(),dev_uc_init(),

(3)ether_setup对网卡设备部分成员初始化

(4)申请收发队列数

netif_alloc_netdev_queues和 netif_alloc_rx_queues为每个队列申请队列头,此处收发各只有1个队列,发送队列的长度为1000

(5)拷贝网卡名dev->name  = eth%d

(6)私有数据定义

structhieth_netdev_local:

             struct sk_buff_head rx_head;    /*received pkgs*/

             struct sk_buff_head rx_hw;  /*rx pkgs in hw*/

             struct sk_buff_head tx_hw;  /*tx pkgs in hw*/

int tx_hw_cnt;

 

2、网卡设备成员进行设置

netdev->watchdog_timeo  = 3*HZ; 网卡传输超时(发送)以jffies为单位

超时后最终会调用hieth_net_timeout这个进行超时处理

netdev->netdev_ops      = &hieth_netdev_ops; 网卡设备操作函数初始化(见第五节)

netdev->ethtool_ops     = &hieth_ethtools_ops; 控制网络接口参数所需函数初始化(见第八节)

 

3、映射网络地址控制器IO空间

ld->iobase = (unsignedlong)ioremap_nocache(CONFIG_HIETH_IOBASE, CONFIG_HIETH_IOSIZE);

 

4、网络端口复位和初始化

/* reset and init port */

hieth_port_reset(ld, ld->port);

hieth_port_init(ld, ld->port);

 

5、硬件发送队列深度

ld->depth.hw_xmitq =CONFIG_HIETH_HWQ_XMIT_DEPTH; (=12)

接收则为:64 - 12 = 52

6、同PHY建立连接phy_connect()

phydev->adjust_link= hieth_adjust_link;

其中接口:获取link , duplex,speed等信息,另外打印是up还是down

 

7、skb队列头初始化

  skb_queue_head_init(&ld->rx_head);

  skb_queue_head_init(&ld->rx_hw);

skb_queue_head_init(&ld->tx_hw);

 

8、申请接收队列缓冲区hieth_init_skb_buffers

调用dev_alloc_skb()原子分配,大小为:1024* 2KB,即skb接收池共1024个skb,每个skb是2KB

Skb申请后对部分参数初始化:atomic_set(&skb->users,1);

 

9、注册网卡设备

register_netdev(netdev);        

 

 

五、网卡操作函数

static const struct net_device_opshieth_netdev_ops = {                                               

    .ndo_open       =hieth_net_open, (见第六节)                                                                

   .ndo_stop       = hieth_net_close,                                                                

    .ndo_start_xmit     =hieth_net_hard_start_xmit,(见第七节)                                                  

   .ndo_tx_timeout     =hieth_net_timeout,                                                           

   .ndo_do_ioctl       =hieth_net_ioctl,                                                            

   .ndo_set_mac_address    =hieth_net_set_mac_address,                                              

   .ndo_get_stats      = hieth_net_get_stats,                                                        

}; 

 

六、hieth_net_open打开网卡

1、基本流程

(1)初始化网卡数据接收函数,使用tasklet机制

ld->bf_recv.func = hieth_bfproc_recv(见第3小节); bf_recv在中断中被调用

bf_recv为tasklet,

(2)hieth_set_hwq_depth(ld);

         硬件接收和发送帧队列深度设置,发送12个,接收52个。

         注:软件至少需要申请与硬件接收深度相同个数的缓冲区,和硬件通过DMA一一映射。

(3)netif_carrier_off(dev);设备载波丢失

(4)hieth_feed_hw(ld);(见第2小节),填充硬件接收队列,硬件接收到的数据直接放入skb(DMA映射)

(5)启动队列netif_start_queue(dev);

(6)启动PHY,and 开启中断

(7)      监控定时器ld->monitor.function = hieth_monitor_func;(100ms)(见第4小节

(8)支持EEE的相关设置

 

2hieth_feed_hw,缓冲区skb首地址依次写入硬件接收队列

将skb通过DMA直接映射给硬件使用(目的减少数据拷贝)

(1).检测是否可配置输入队列帧首地址

(2).从接收队列缓冲池中查找空闲的skb

hieth_platdev_alloc_skb()中atomic_inc(&skb->users)

这时skb->users=2,这样网络层就不会释放skb。

(3).将该空闲skb通过DMA映射提供给网卡接收数据DMA_FROM_DEVICE

(4).将缓冲区skb首地址写入输入帧首地址寄存器IQ_ADDR

(5).将skb放入接收硬件接收队列中,便于管理

注意:这是一个while循环直到不可配置为止,将硬件可配置队列(52个)填满为止。

(6)dma_map_single注释:流式DMA映射

映射一块处理器的虚拟地址,这样可以让外设访问。该函数返回内存的物理地址。

DMA_NONE    仅用于调试目的
DMA_TO_DEVICE    数据从内存传输到设备,可认为是写操作。
DMA_FROM_DEVICE    数据从设备传输到内存,可认为是读操作。
DMA_BIDIRECTIONAL    不清楚传输方向则可用该类型。

疑问1:写寄存器不断覆盖,是否只有最后一次有效。

解答1:每写一次就加入硬件管理队列,直至写满为止(根据是否可以配置输入队列地址来判断)

疑问2dma映射后没有找到unmap

 

3、数据接收函数hieth_bfproc_recv(中断调用bf_recv

(1)hieth_hw_recv_tryup将硬件队列数据映射到内存

A.查询中断状态是否有帧等待CPU接收

B.获取帧长度

C.从硬件队列获取数据后,添加到接收队列头

skb = skb_dequeue(&ld->rx_hw);

skb_queue_tail(&ld->rx_head, skb);

(2)从接收队列头获取skb

(3)对接收到数据进行简单判断

4)传递给上层协议处理netif_rx(skb);

注意:循环操作

疑问:skb源自缓冲池,只在probe时申请,如果释放了又没有申请,为啥还可以使用。

答案:结合4.8小节和6.2小节控制skb->users来达到目的,只有当skb->usersfree_skb才会释放skb

 

4hieth_monitor_func定时监控函数

(1)填充硬件接收队列

hieth_feed_hw(ld);

(2)释放队列已经发送的skb

hieth_xmit_release_skb(ld);

 

七、数据发送hieth_net_hard_start_xmit(由中断启动)

1、 释放队列已经发送的skb  hieth_xmit_release_skb(ld);

(1)检查输出队列出队index是否小于硬件发送队里数

如果小于,说明已经有发送完成的,那么就需要进行skb释放

(2)从硬件队列删除已发送skb

skb_dequeue(&ld->tx_hw);

(3)释放skb

注意:循环操作

2、用户数据skb进行dma映射DMA_TO_DEVICE

3、发送用户数据hieth_xmit_real_send(ld,skb);

(1)检查是否可以配置输出队列

(2)将输出帧skb首地址写入寄存器

(3)将输出帧skb加入硬件发送队列。

4、清中断,启动硬件发送

在未发送完成前,告诉内核停止发送队列:netif_stop_queue(dev);

使能中断,发送完成后进入中断:hieth_irq_enable(ld,UD_BIT_NAME(HIETH_INT_TXQUE_RDY));

注:当硬件发送完成后会产生中断,中断里会告知内核重新启动发送,禁止中断见(9.2节)

 

八、网卡设备设备操作函数

staticstruct ethtool_ops hieth_ethtools_ops = {

        .get_drvinfo            = hieth_ethtools_get_drvinfo,

        .get_link               = hieth_ethtools_get_link,

        .get_settings           = hieth_ethtools_get_settings,

        .set_settings           = hieth_ethtools_set_settings,

}; 

 

九、注册网卡中断: request_irq(hieth_net_isr)

中断的主要目的就是接收和处理网卡数据,传递给协议层。

ret= request_irq(CONFIG_HIETH_IRQNUM, hieth_net_isr, IRQF_SHARED,"hieth", hieth_devs_save);

中断号:56,

IRQF_SHARED:与其他设备共享中断号主要是上行和下行端口

中断处理函数:hieth_net_isr

网卡设备:hieth_devs_save,定义如下:struct net_device*hieth_devs_save[2]指向上行和下行口

1、hieth_net_isr

(1)屏蔽所有中断

(2)读取中断状态

3)中断处理hieth_net_isr_proc(见第2个小节)

(4)清楚中断状态

(5)       开启使能各个中断掩码

 

2、中断处理hieth_net_isr_proc

产生中断的情况:新报文到,或者外出报文发送完成

(1)处理接收到的数据

         根据处理器,收到8个数据包后产生一个中断,如果没有8个数据包则根据超时产生中断。

         将网卡数据映射到内存skb,见6.3节:

                   hieth_hw_recv_tryup(ld)

         然后启动tasklet机制,启动后半部操作:

                   tasklet_schedule(&ld->bf_recv);具体操作函数见6.3

(2)硬件发送完成,中断禁止,告之内核可以重启启动发送队列

         hieth_irq_disable(ld,UD_BIT_NAME(HIETH_INT_TXQUE_RDY));

    netif_wake_queue(hieth_devs_save[ld->port]);

         发送数据见第七节,最后会启动中断。

 

十、总结

该驱动通过中断来处理帧的接收和发送。接收时有底层申请好缓冲池,循环利用从不释放。没有使用NAPI机制。比较简单的网络数据处理。