zoukankan      html  css  js  c++  java
  • 17、网卡驱动程序-DM9000举例

    (参考:cs89x0.c可以参考)

    DM9000 芯片实现网络功能的基础,在接收数据时采用中断方式,即当有数据到来并在 DM9000 内部 CRC 校验通过后会产生一个接收中断;

    网卡驱动程序框架:

    app:  socket

    --------------------------------------------------           

         ---------------           

        --------------- 若干层网络协议--纯软件           

         ---------------           

        ---------------

    hard_start_xmit(解析sk_buff结构体,把真实数据发送出去)  ||     /               

                                                   /     ||  netif_rx   sk_buff (构造sk_buff结构体,上传)          

         ---------------          

        硬件相关的驱动程序(要提供hard_start_xmit, 有数据时要用netif_rx上报)          

         --------------------------------------------------               

               硬件                     

    怎么写网卡驱动程序?

    1. 分配一个net_device结构体

    2. 设置:

    2.1 发包函数: hard_start_xmit

    2.2 收到数据时(在中断处理函数里)用netif_rx上报数据

    2.3 其他设置

    3. 注册: register_netdev()

    测试1th/2th:

    1. insmod virt_net.ko

    2. ifconfig vnet0 3.3.3.3   

      ifconfig // 查看

    3. ping 3.3.3.3  // 成功(能成功是因为,ping3.3.3.3的时候先看前面3.3.3.X去寻找同一网段的网卡,在发出去,所以当主机有多个网卡的时候,不要设置为同一个网段,否则程序不知道发给那个网卡)

         ping 3.3.3.4  // 死机

    (网卡的接口地址和内存一样,直接访问就可以)

    (JZ2440中DM9000的片选信号是nGCS4,对应的地址是0x20000000~0x28000000,只要CPU发出的地址位于这区间的地址,nGCS4就会被置起来)

    (2440与通过内存控制器与DM9000通讯的,那这个内存控制器读写DM9000的时序应该在init中设置,本驱动没设置的原因是uboot中已经设置好了)

    测试DM9000C驱动程序:

    1. 把dm9dev9000c.c放到内核的drivers/net目录下

    2. 修改drivers/net/Makefile

    把 obj-$(CONFIG_DM9000) += dm9000.o

    改为 obj-$(CONFIG_DM9000) += dm9dev9000c.o

    3. make uImage   

    使用新内核启动

    4. 使用NFS启动 或 ifconfig eth0 192.168.1.17

    ping 192.168.1.1  

    (DM9000能像内存一样读写的原因是其数据线和cpu数据线相连,当访问nGCS4的任何地址区域,发给网卡的不管是地址还是数据,在CPU的角度都是数据)

    1.首先来看DM9000C原理图

    如下图所示:

     

      (#:表示低电平有效)

    • SD0~15: 16位数据线,有CMD引脚决定访问类型
    • CMD:      命令线,当CMD为高,表示SD 传输的是数据,CMD为低表示传输的是地址
    • INT:         中断引脚,接在2440的GPF7脚上
    • IOR#:      读引脚,接在2440的nOE脚上
    • IOW#:     写引脚,接在2440的nWE脚上
    • CS#:       片选,放在2440的bank4的片选上面

    1.1其中2440手册的bank4地址区间如下图:

     

    bank4的区间位于: 0X20000000~0X28000000,当我们访问这个区间的地址,内存控制器便会使能网卡DM9000C的使能脚,所以我们的DM9000C的io基地址=0X20000000

    其中DM9000C的CMD引脚接在bank4的LADDR2上面

    比如当在地址0X2000 0000上读写数据时,表示读写的数据是DM9000C的地址

    访问的地址0X2000 0004上读写数据时,表示读写的数据是DM9000C的数据

    1.2 DM9000C收发过程

    当DM9000C收到外部的数据后,会暂存到内部地址中,然后产生一个上升沿中断,等待2440读取数据

    当DM9000C将2440的数据转发出去后,也会产生一个上升沿中断给2440

    如下图所示,DM9000C的中断引脚位于pin34脚

     

    接在2440的GPF7引脚上,使用的中断为EINT7

     

    接下来便来修改厂家提供的DM9000C源代码

    2.发现它的init_module()入口函数前有个条件编译

    所以要注释掉 “#ifdef MODULE” 和 “#endif”

    3.修改入口出口函数名,并修饰它们(修改函数名避免与内核的其它函数重名)

     

    4.修改驱动的硬件相异性(设置基地址,寄存器,中断等)

    4.1先来找找代码在哪里初始化DM9000C硬件的

    进入dm9000c_init()

           -> dmfe_probe()

    其中dmfe_probe()函数如下:

    复制代码
    struct net_device * __init dmfe_probe(void)
    {
    struct net_device *dev;
    
    dev= alloc_etherdev(sizeof(struct board_info));          //分配一个net_device结构体
    ... ...
    
    err = dmfe_probe1(dev);                                 //调用dmfe_probe1()函数
    ... ...
    
    err = register_netdev(dev);                            //向内核注册net_device结构体
    ... ...
    }
    复制代码

    显然dmfe_probe1()函数就是用来初始化DM9000C硬件和设置net_device结构体的成员用的

    4.2 进入dmfe_probe1()函数

    如下图,这个iobase 变量就是我们DM9000C的io基地址0x20000000

     

    iobase的作用:

    如上图, 读一次DM9000C的VID低字节之前,需要先将地址赋为0x20000000,也就是将DM9000C的CMD置0,然后向0x20000000写入要读的DM9KS_VID_L地址值

    最后再将地址+4,也就是赋为0x20000100,将CMD置1,然后读出0x20000100的值,也就是DM9000C的VID低字节

    DM9000C的读写方式都是这样的,先将CMD置0,写入DM9000C的地址,然后再将CMD置1,来读写数据

    4.3所以在init函数中便重新设置iobase 变量,其中iobase是int型

     

    并在exit出口函数中,添加iounmp()

     

    4.4 继续进入dmfe_probe1()函数,往下看

    如下图所示,屏蔽红框里的代码,该代码用来核对版本,我们的DM9000C版本号不一样,所以要屏蔽

    4.5 在init函数中,修改中断名,将irq改为IRQ_EINT7

     

    4.6 修改中断

    当使用了register_netdev()注册了网卡驱动net_device后,在内核中使用ifconfig就会进入net_device->open成员函数申请中断,激活队列等

    所以我们要修改open成员函数的申请中断函数,将触发中断改为“IRQT_RISING”,上升沿触发

     

    5.接下来便开始设置2440的存储控制寄存器

    设置2440的bank4的硬件位宽,时序,因为不同的硬件,涉及的数据收发都不同,

    5.1设置BWSCON总线宽度控制寄存器

    我们只设置BANK4的内容,所以只有下面3个(BANK0的位宽由OM[1:0] 硬件设置)

     

    设置ST4=0,不使用UB/LB(UB/LB:表示高字节与低字节数据是否分开传输)

    设置WS4=0,其中WAIT引脚为PE4,而我们DM9000C没有引脚接入PE4,所以禁止

    设置DW4=0x01,我们的DM9000C的数据线为16位

    5.2设置BANKCON4控制寄存器

     

     设置这些时序之前,首先来看DM9000C芯片手册时序图和2440的时序图

     

    参考上图,得出BANKCON4 寄存器设置如下(HCLK=100MHZ,1个时钟等于10ns)

    • 设置Tacs=0,因为CS和CMD可以同时结束(bank4地址(也就是CMD)稳定多久后,CS片选才启动)
    • 设置Tcos=T1=0(CS片选后,多久才能使能读写)
    • 设置Tacc=T2>=10ns=1,表示2个时钟 (access cycle ,读写使能后,多久才能访问数据)
    • 设置Tcoh=T4>=3ns=1,表示1个时钟 ,因为当DM9000的写信号取消后,数据线上的数据还需要至少3ns才消失(nOE读写取消后,片选需要维持多长时间)
    • 设置Tcah=0,因为 CS和CMD可以同时结束 (CS片选取消后,地址(也就是CMD)需要维持多长时间)

    代码如下图,在init入口函数中设置

     

    (PS:若DM9000C无法驱动,可能是Tacc时间太短,导致读取不到数据,可以将Tacc设大一点)

    与硬件相关的部分已经改好了,接下来开始编译

    6.编译测试

    编译之前,首先添加该驱动需要的内核头文件:

    #include <asm/delay.h>
    #include <asm/irq.h>
    #include <linux/irq.h>
    #include <asm/io.h>
    #include <asm/arch-s3c2410/regs-mem.h>

    编译无误后,便开始测试DM9000C驱动程序:

    1) 把dm9dev9000c.c放到内核的drivers/net目录下,来替换原来内核的DM9000C

    2) 修改drivers/net/Makefile 


    obj-$(CONFIG_DM9000) += dm9000.o
    改为(如下图所示)
    obj-$(CONFIG_DM9000) += dm9dev9000c.o

     

    3) make uImage

    如下图,说明新的驱动已编译进内核

     

    4) 使用新内核启动

    ifconfig eth0 192.168.2.107

    ping 192.168.2.1

    如下图,可以ping通,说明移植成功

     

  • 相关阅读:
    安装vue-cli时-4058报错的解决方法
    Sublime text 3 配置
    Vue项目本地run与build后样式不同,build后样式不生效
    npm run build 打包后,如何运行在本地查看效果(Nginx服务)
    JavaScript(ES6)学习笔记-Set和Map与数组和对象的比较(二)
    JavaScript(ES6)学习笔记-Set和Map数据结构(一)
    Angular
    本地项目与码云上的项目相关联
    获取GitHub上远程分支内容
    MyEclipse
  • 原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/8619945.html
Copyright © 2011-2022 走看看