dm9000驱动 linux_linux dm9000 驱动 - CSDN
  • 说明1:本文分析基于内核源码版本为linux-2.6.31 ...虽然Linux驱动程序应该是和具体的硬件平台分离的,但是为了更好的理解DM9000驱动程序,这里还是结合一下Mini2440开发板,这样也可以更好的体会如何

    版权声明: 可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息。

    说明1:本文分析基于内核源码版本为linux-2.6.31
    说明2:本文在理解了linux中总线、设备和驱动模型的基础上加以分析代码

     

    虽然Linux驱动程序应该是和具体的硬件平台分离的,但是为了更好的理解DM9000的驱动程序,这里还是结合一下Mini2440开发板,这样也可以更好的体会如何实现驱动和平台分离。

    本文分成以下几个部分:
    一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系。
    二、两个重要的结构体介绍:sk_buff和net_device
    三、具体代码分析

     

     

    一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系
    Mini2440开发板上DM9000与S3C2440的连接关系如下:  
     
    其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x20000000开始。DM9000的TXD[2:0]作为strap pin在电路图中是空接的,所以IO base是300H。中断使用了EINT7。这些内容在Mach文件中有如下体现:

    另外在Mach文件中还定义了DM9000平台设备,设备名称为“dm9000”,设备资源就是上面定义的IO和中断资源。代码清单如下:

    这个DM9000平台设备作为众多平台设备中的一个在扳子初始化的时候就被添加到了总线上。代码清单如下:

      

      

     

    二、两个重要的结构体简单介绍:sk_buff和net_device

       *sk_buff

        如果把网络传输看成是运送货物的话,那么sk_buff就是这个“货物”了,所有经手这个货物的人都要干点什么事儿,要么加个包装,要么印个戳儿等等。收货的时候就要拆掉这些包装,得到我们需要的货物(payload data)。没有货物你还运输什么呢?由此可见sk_buff的重要性了。关于sk_buff的详细介绍和几个操作它的函数,参考本博客转载的一篇文章:“linux内核sk_buff的结构分析”,写得非常明白了。赞一个~

      *net_device

        又是一个庞大的结构体。好吧,我承认我从来就没有看全过这个结构体。它在内核中就是指代了一个网络设备。驱动程序需要在探测的时候分配并初始化这个结构体,然后使用register_netdev来注册它,这样就可以把操作硬件的函数与内核挂接在一起。

       

    三、具体代码的分析
       在顺序分析之前先看三个结构体变量和一个自定义的结构体。

       * dm9000_driver变量。是platform_driver结构体变量,其中包含了重要的:驱动的名字(用来match)和几个重要操作函数。

      

      * dm9000_netdev_ops变量。是net_device_ops结构体变量, 其中定义了操作net_device的重要函数,我们在驱动程序中根据需要的操作要填充这些函数。代码清单如下:

     

       * dm9000_ethtool_ops变量。是ethtool_ops结构体变量,为了支持ethtool,其中的函数主要是用于查询和设置网卡参数(当然也有的驱动程序可能不支持ethtool)。代码清单如下:

     

       * board_info结构体。用来保存芯片相关的一些私有信息。具体在代码中分析。下面是这个结构体的清单。

     

     

    下面看一下具体代码。

    分析代码还是从init顺序开始。

         1. 注册平台驱动。

        主要完成的任务是:将驱动添加到总线上,完成驱动和设备的match,并执行驱动的probe函数。代码清单如下:

     

        2. probe函数。

       主要完成的任务是:探测设备获得并保存资源信息,根据这些信息申请内存和中断,最后调用register_netdev注册这个网络设备。以下是代码清单,可以分成几个部分来看:

       1) 首先定义了几个局部变量:

             struct dm9000_plat_data *pdata = pdev->dev.platform_data;
             struct board_info *db; /* Point a board information structure */
             struct net_device *ndev;

       2) 初始化一个网络设备。关键系统函数:alloc_etherdev()

       3) 获得资源信息并将其保存在board_info变量db中。关键系统函数:netdev_priv(),  platform_get_resource()

       4) 根据资源信息分配内存,申请中断等等, 并将申请后的资源信息也保存到db中,并且填充ndev中的参数。 关键系统函数:request_mem_region(),  ioremap()。 自定义函数:dm9000_set_io()

       5) 完成了第4步以后,回顾一下db和ndev中都有了什么:

           struct board_info *db:

                     addr_res -- 地址资源

                     data_res -- 数据资源

                     irq_res    -- 中断资源

                     addr_req -- 分配的地址内存资源

                     io_addr   -- 寄存器I/O基地址

                     data_req -- 分配的数据内存资源

                     io_data   -- 数据I/O基地址

                     dumpblk  -- IO模式

                     outblk     -- IO模式

                     inblk        -- IO模式

                     lock         -- 自旋锁(已经被初始化)

                     addr_lock -- 互斥锁(已经被初始化)

            struct net_device *ndev:

                     base_addr  -- 设备IO地址

                     irq              -- 设备IRQ号

         6) 设备复位。硬件操作函数dm9000_reset()

         7) 读一下生产商和制造商的ID,应该是0x9000 0A46。 关键函数:ior()

         8) 读一下芯片类型。

         ========以上步骤结束后我们可以认为已经找到了DM9000========

         9) 借助ether_setup()函数来部分初始化ndev。因为对以太网设备来讲,很多操作与属性是固定的,内核可以帮助完成。

        10) 手动初始化ndev的ops和db的mii部分。

        11) (如果有的话)从EEPROM中读取节点地址。这里可以看到mini2440这个板子上没有为DM9000外挂EEPROM,所以读取出来的全部是0xff。见函数dm9000_read_eeprom。 关于外挂EEPROM,可以参考datasheet上的7.EEPROM Format一节。

        12)  很显然ndev是我们在probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将ndev保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。

        13) 使用register_netdev()注册ndev。

    下面是代码清单:

     

        3. platform_driver的remove, suspend和resume的实现

            remove函数的功能是把设备从内核中移除,释放内存区域。该函数在卸载模块时被调用。代码清单如下:

     

            suspend函数并不真正把设备从内核中移除,而只是标志设备为removed状态,并设置挂起标志位,最后关闭设备。代码清单如下:

          resume函数将挂起的设备复位并初始化,软后将设备标志为attached状态,并设置挂起标志位。代码清单如下:

     

         4. 下面看一下用于填充net_device中netdev_ops和ethtool_ops的一些函数。

        代码在上面已经写出来了,为了看着方便在下面再写一遍,可以看出虽然mini2440的板子上没有为DM9000挂EEPROM,但这里还是定义了操作EEPROM的函数。就是说写驱动的时候是不考虑具体的板子的,你板子用不用是你的事,但是我们的驱动应该所有的功能都考虑进去。这也体现了驱动和平台分离的设计思想。

      

        *dm9000_open()

        进行的工作有 向内核注册中断,复位并初始化dm9000,检查MII接口,使能传输等。代码清单如下:

       

        *dm9000_stop()

         做的工作基本上和open相反。代码清单如下:

     

        *dm9000_start_xmit()

        重要的发送数据包函数。从上层发送sk_buff包。在看代码之前先来看一下DM9000是如何发送数据包的。

       

    如上图所示,在DM9000内部SRAM中,地址0x0000~0x0BFF是TX Buffer, 地址0x0C00~0x3FFF是RX Buffer。在发送一个包之前,包中的有效数据必须先被存储到TX Buffer中并且使用输出端口命令来选择MWCMD寄存器。包的长度定义在TXPLL和TXPLH中。最后设置TXCR寄存器的bit[0] TXREQ来自动发送包。如果设置了IMR寄存器的PTM位,则DM9000会产生一个中断触发在ISR寄存器的bit[1]=PTS=1, 同时设置一个完成标志在NSR寄存器的bit[2]=TX1END或者 bit[3]=TX2END,表示包已经发送完了。发送一个包的具体步骤如下:

    Step 1: 检查存储数据宽度。通过读取中断状态寄存器(ISR)的bit[7:6]来确定是8bit,16bit还是32bit。

    Step 2: 写数据到TX SRAM中。

    Step 3: 写传输长度到TXPLL和TXPLH寄存器中。

    Step 4: 设置TXCR寄存器的bit[0]TXREQ来开始发送一个包。

     

    代码清单如下,让我们看看在获得自旋锁这段期间都干了些什么:

     

        *dm9000_timeout()

        当watchdog超时时调用该函数。主要的功能是保存寄存器地址,停止队列,重启并初始化DM9000,唤醒队列,恢复寄存器地址。

        代码清单如下:

     

        *dm9000_hash_table()

        该函数用来设置DM9000的组播地址。代码清单如下:

     

        *dm9000_ioctl()

        从源码可以看出,dm9000的ioctl实际上是使用了mii的ioctl。代码清单如下:

     

        *dm9000_poll_controller()

        当内核配置Netconsole时该函数生效。代码清单如下:

     

        *dm9000_get_drvinfo()

        该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool_drvinfo结构体变量。代码清单如下:

     

        *dm9000_get_settings()

        该函数得到由参数cmd指定的设置信息。

        *dm9000_set_settings()

        该函数设置由参数cmd指定的信息。

        *dm9000_get_msglevel()

        *dm9000_set_msglevel()

        这两个函数设置和取得message level,实际是设置和取得board_info中的msg_enable信息。

        *dm9000_nway_reset()

        重启mii的自动协商

        *dm9000_get_link()

        该函数的到link状态。如果带外部PHY,则返回mii链接状态。 否则返回DM9000 NSR寄存器数值。

        *dm9000_get_eeprom_len()
          dm9000_get_eeprom()
          dm9000_set_eeprom()

        这三个函数用来读写eeprom。

     

       5. 与数据传输有关的函数。

         上面已经分析了一个与数据传输有关的函数,那就是发送数据的函数dm9000_start_xmit()。这里再来分析数据的接收。再看具体代码之前还是来看看DM9000的数据接收的过程。

          接收的数据存储在RX SRAM中,地址是0C00h~3FFFh。存储在RX_SRAM中的每个包都有4个字节的信息头。可以使用MRCMDX和MRCMD寄存器来得到这些信息。第一个字节用来检查数据包是否接收到了RX_SRAM中,如果这个字节是"01",意味着一个包已经接收。如果是"00",则还没有数据包被接收到RX_SRAM中。第二个字节保存接收到的数据包的信息,格式和RSR寄存器一样。根据这个格式,接收到的包能被校验是正确的还是错误的包。第三和第四字节保存了接收的数据包的长度。这四个字节以外的其他字节就是接收包的数据。看下图可以更好的理解这种格式。

    根据包的结构可以知道接收一个包应该按照下面的步骤来进行:

    第一步:判断包是否已经接收过来了。需要用到MRCMDX寄存器。MRCMDX寄存器是存储数据读命令寄存器(地址不增加)。 这个寄存器只是用来读接收包标志位"01"。下面这段代码是一个例子,用来判断RX ready:

     

     第二步:检查包的状态和长度。需要用到MRCMD寄存器(存储数据读命令,读指针自动增加)。下面这段例子代码用来读RX状态和长度。

    第三步:读包的数据。也需要MRCMD寄存器。例子代码如下:

     

     

    下面的dm9000_rx()函数实际上是按照上面这三个步骤来实现的,具体实现并不一定是要参照例子代码。 注意这里按照DM9000接收包的格式定义了一个结构体dm9000_rxhdr用来表示头部的四个字节。代码清单如下:

    接收函数代码如下:

     

       6. 中断处理相关函数

       DM9000的驱动程序采用了中断方式而非轮询方式。触发中断的时机发生在:1)DM9000接收到一个包以后。2)DM9000发送完了一个包以后。

       中断处理函数在open的时候被注册进内核。代码清单如下:

       *dm9000_tx_done()

       注:dm9000可以发送两个数据包,当发送一个数据包产生中断后,要确认一下队列中有没有第2个包需要发送。

        (1)读取dm9000寄存器NSR(Network Status Register)获取发送的状态,存在变量tx_status中;

        (2)如果发送状态为NSR_TX2END(第2个包发送完毕)或者NSR_TX1END(第1个包发送完毕),则将待发送的数据包数量(db->tx_pkt_cnt)减1,已发送的数据包数量(dev->stats.tx_packets)加1

        (3)检查变量db->tx_pkt_cnt(待发送的数据包)是否大于0(表明还有数据包要发送),则调用函数dm9000_send_packet发送队列中的数据包;

        (4)调用函数netif_wake_queue(dev)通知内核可以将待发送的数据包进入发送队列。

     

     

       7. 一些操作硬件细节的函数。

       在看函数之前还是先来看一下DM9000 CMD Pin 和Processor并行总线的连接关系。CMD管脚用来设置命令类型。当CMD管脚拉高时,这个命令周期访问DATA_PORT。 如果拉低, 则这个命令周期访问ADDR_PORT。见下图:

    当然,内存映射的I/O空间读写还是采用最基本的readb(), readw(), readl(), writeb(), writew(), writel() , readsb(), readsw(), readsl(), writesb(), writesw(), writesl() 。

    在DM9000的驱动中还自定义了几个函数,方便操作。

         * ior()

            从IO端口读一个字节。代码清单如下:

     

        * iow()

            向IO端口写一个字节。代码清单如下:

     

    此外还有dm9000_outblk_8bit(), dm9000_outblk_16bit(), dm9000_outblk_32bit(), dm9000_inblk_8bit(), dm9000_inblk_16bit(), dm9000_inblk_32bit()等等。不一一解释。

     

     

    ======================THE END======================

    展开全文
  • 包括linux wince 单片机的DM9000A的官方驱动 包括linux wince 单片机的DM9000A的官方驱动
  • linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析 【linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析  【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的...

    【linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析


    【linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析 

    【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏 

    【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析 

    【linux驱动分析】之dm9000驱动分析(四):net_device结构体 

    【linux驱动分析】之dm9000驱动分析(五):另外几个重要的结构体 

    【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 

    【linux驱动分析】之dm9000驱动分析(七):dm9000的卸载挂起和恢复以及打开和停止


    一、了解几个概念
    1、MAC
        MAC是Media Access Control 的缩写,即媒体访问控制子层协议,又翻译为媒体接入控制器。
        该协议位于OSI七层协议中数据链路层的下半部分,主要负责控制与连接物理层的物理介质。在发送数据的时候,MAC协议可以事先判断是否可以发送数据,如果可以发送将给数据加上一些控制信息,最终将数据以及控制信息以规定的格式发送到物理层;在接收数据的时候,MAC协议首先判断输入的信息并是否发生传输错误,如果没有错误,则去掉控制信息发送至LLC层。以太网MAC由IEEE-802.3以太网标准定义。
    2、MII
        MII即媒体独立接口, “媒体独立”表明在不对MAC硬件重新设计或替换的情况下,任何类型的PHY设备都可以正常工作。包括分别用于发送器和接收器的两条独立信道。每条信道都有自己的数据、时钟和控制信号。MII数据接口总共需要16个信号,包括TX_ER,TXD<3:0>,TX_EN,TX_CLK,COL,RXD<3:0>,RX_EX,RX_CLK,CRS,RX_DV等。
        MII以4位半字节方式传送数据双向传输,时钟速率25MHz。其工作速率可达100Mb/s。MII管理接口是个双信号接口,一个是时钟信号,另一个是数据信号。通过管理接口,上层能监视和控制PHY。其管理是使用SMI(Serial Management Interface)总线通过读写PHY的寄存器来完成的。PHY里面的部分寄存器是IEEE定义的,这样PHY把自己的目前的状态反映到寄存器里面,MAC通过SMI总线不断的读取PHY的状态寄存器以得知目前PHY的状态,例如连接速度,双工的能力等。当然也可以通过SMI设置PHY的寄存器达到控制的目的,例如流控的打开关闭,自协商模式还是强制模式等。不论是物理连接的MII总线和SMI总线还是PHY的状态寄存器和控制寄存器都是有IEEE的规范的,因此不同公司的MAC和PHY一样可以协调工作。当然为了配合不同公司的PHY的自己特有的一些功能,驱动需要做相应的修改。

    3、PHY
        PHY是物理接口收发器,它实现物理层。包括MII/GMII(介质独立接口)子层、PCS(物理编码子层)、PMA(物理介质附加)子层、PMD(物理介质相关)子层、MDI子层。
        100BaseTX采用4B/5B编码。PHY在发送数据的时候,收到MAC过来的数据(对PHY来说,没有帧的概念,对它来说,都是数据而不管什么地址,数据还是CRC),每4bit就增加1bit的检错码,然后把并行数据转化为串行流数据,再按照物理层的编码规则把数据编码,再变为模拟信号把数据送出去。收数据时的流程反之。PHY还有个重要的功能就是实现CSMA/CD的部分功能。它可以检测到网络上是否有数据在传送,如果有数据在传送中就等待,一旦检测到网络空闲,再等待一个随机时间后将送数据出去。如果两个碰巧同时送出了数据,那样必将造成冲突,这时候,冲突检测机构可以检测到冲突,然后各等待一个随机的时间重新发送数据。这个随机时间很有讲究的,并不是一个常数,在不同的时刻计算出来的随机时间都是不同的,而且有多重算法来应付出现概率很低的同两台主机之间的第二次冲突。通信速率通过双方协商,协商的结果是两个设备中能同时支持的最大速度和最好的双工模式。这个技术被称为Auto Negotiation或者NWAY。隔离变压器把PHY送出来的差分信号用差模耦合的线圈耦合滤波以增强信号,并且通过电磁场的转换耦合到连接网线的另外一端。RJ-45中1、2是传送数据的,3、6是接收数据的。新的PHY支持AUTO MDI-X功能(也需要隔离变压器支持)。它可以实现RJ-45接口的1、2上的传送信号线和3、6上的接收信号线的功能自动互相交换

        网卡工作在osi的最后两层,物理层和数据链路层,物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。物理层的芯片称之为PHY。数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。以太网卡中数据链路层的芯片称之为MAC控制器。很多网卡的这两个部分是做到一起的。他们之间的关系是pci总线接mac总线,mac接phy,phy接网线(当然也不是直接接上的,还有一个变压装置)。
    MAC 和PHY 一个是数据链路层 一个是物理层 两者通过MII传送数据。


    4、曼彻斯特编码

        曼彻斯特编码又称曼彻斯特相位编码,它通过相位变化来实现每个位(图2)。通常,用一个时钟周期中部的上升沿表示“1”,下降沿表示“0”。周期末端的相位变化可忽略不计,但有时又可能需要将这种相位变化计算在内,这取决于前一位的值。


    5、4B/5B编码

        4B/5B编码是一种块编码方式。它将一个4位的块编码成一个5位的块。这就使5位块内永远至少包含2个“1”转换,所以在一个5位块内总能进行时钟同步。该方法需要25%的额外开销。

    二、DM9000

    1、框图

    2、芯片引脚图
    MII接口和32位总线模式可以配置
    (1)、MII接口

    (2)、32位数据总线



    【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏

    硬件平台:友善之臂Tiny6410核心板 + DM9000EP
    软件平台:linux-2.6.38
    交叉编译器:Friendly ARM提供的arm-linux-gcc 4.5.1
    一、源代码(mach-mini6410.c)
     1 /* Ethernet */
     2 #ifdef CONFIG_DM9000
     3 #define S3C64XX_PA_DM9000    (0x18000000)
     4 #define S3C64XX_SZ_DM9000    SZ_1M
     5 #define S3C64XX_VA_DM9000    S3C_ADDR(0x03b00300)
     6 
     7 static struct resource dm9000_resources[] = {
     8     [0] = {
     9         .start        = S3C64XX_PA_DM9000,
    10         .end        = S3C64XX_PA_DM9000 + 3,
    11         .flags        = IORESOURCE_MEM,
    12     },
    13     [1] = {
    14         .start        = S3C64XX_PA_DM9000 + 4,
    15         .end        = S3C64XX_PA_DM9000 + S3C64XX_SZ_DM9000 - 1,
    16         .flags        = IORESOURCE_MEM,
    17     },
    18     [2] = {
    19         .start        = IRQ_EINT(7),
    20         .end        = IRQ_EINT(7),
    21         .flags        = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
    22     },
    23 };
    24 
    25 static struct dm9000_plat_data dm9000_setup = {
    26     .flags            = DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY,
    27     .dev_addr        = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
    28 };
    29 
    30 static struct platform_device s3c_device_dm9000 = {
    31     .name            = "dm9000",
    32     .id                = 0,
    33     .num_resources    = ARRAY_SIZE(dm9000_resources),
    34     .resource        = dm9000_resources,
    35     .dev            = {
    36         .platform_data = &dm9000_setup,
    37     }
    38 };
    39 
    40 static int __init dm9000_set_mac(char *str) {
    41     unsigned char addr[6];
    42     unsigned int val;
    43     int idx = 0;
    44     char *p = str, *end;
    45 
    46     while (*p && idx < 6) {
    47         val = simple_strtoul(p, &end, 16);
    48         if (end <= p) {
    49             /* convert failed */
    50             break;
    51         } else {
    52             addr[idx++] = val;
    53             p = end;
    54             if (*p == ':'|| *p == '-') {
    55                 p++;
    56             } else {
    57                 break;
    58             }
    59         }
    60     }
    61 
    62     if (idx == 6) {
    63         printk("Setup ethernet address to %pM\n", addr);
    64         memcpy(dm9000_setup.param_addr, addr, 6);
    65     }
    66 
    67     return 1;
    68 }
    69 
    70 __setup("ethmac=", dm9000_set_mac);
    71 #endif
    72 
    73 static struct map_desc mini6410_iodesc[] = {
    74     {
    75         /* LCD support */
    76         .virtual    = (unsigned long)S3C_VA_LCD,
    77         .pfn        = __phys_to_pfn(S3C_PA_FB),
    78         .length     = SZ_16K,
    79         .type       = MT_DEVICE,
    80     },
    81 #ifdef CONFIG_DM9000           /*这里的定义不知道是做什么用的*/
    82     {
    83         .virtual    = (u32)S3C64XX_VA_DM9000,
    84         .pfn        = __phys_to_pfn(S3C64XX_PA_DM9000),
    85         .length        = S3C64XX_SZ_DM9000,
    86         .type        = MT_DEVICE,
    87     },
    88 #endif
    89 };
    DM9000的设备会在
    platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices));
    里统一注册。

    二、下面分析一下上面代码中红色的宏或者函数
    1、ARRAY_SIZE
      #define  ARRAY_SIZE(arr)   (sizeof(arr) / sizeof( (arr)[0] )  +  __must_be_array(arr) )
    它是定义在include/linux/kernel.h中的一个宏,用来计算数组中元素的个数。
    __must_be_array是编译器相关的,用来防止传入的参数不是数组,比如说传入了指针,这样的话可能回编译不通过(猜测)。
    2、simple_strtoul
    /**
     * simple_strtoul - convert a string to an unsigned long
     * @cp: The start of the string
     * @endp: A pointer to the end of the parsed string will be placed here
     * @base: The number base to use
     */
    unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
    simple_strtoul是定义在lib/vsprintf.c中的函数,它的作用是把一个字符串转换为unsigned long型的整数,并返回。
    其中的endp参数存放解析后的字符串地址,base参数,是要转换的进制数。
    vsprintf.c里还定义了其他好多字符串处理的函数,具体用到时去查。

    3、__setup
    它是定义在include/linux/init.h中的一个宏:
    #define __setup(str, fn)                    \
        __setup_param(str, fn, fn, 0)

    其中:str是关键字,fn是关联处理函数。__setup只是告诉内核在启动时输入串中含有str时,内核要去执行fn。Str必须以“=”符结束以使parse_args更方便解析。紧随“=”后的任何文本都会作为输入传给fn。

    例如本例中的:__setup("ethmac=", dm9000_set_mac);
    关于__setup的更多分析见《__setup宏的作用》


    【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

    源码分析
    sk_buff_head和sk_buff定义在include/linux/skbuff.h中,下面是linux-2.6.38中的定义。
    1、在内核中sk_buff是一个网络数据包,它是一个双向链表,而链表头就是sk_buff_head。
    而sk_buff的内存布局可以分作3个段,第一个就是sk_buff自身,第二个是linear-data buff,第三个是paged-data buff(也就是skb_shared_info)。
    先来看一下sk_buff_head:
    struct sk_buff_head {
        /* These two members must be first. */
        struct sk_buff    *next;
        struct sk_buff    *prev;
    
        __u32        qlen;
        spinlock_t    lock;
    };
    这里可以看到前两个域是和sk_buff一致的,而且内核的注释是必须放到最前面。这里的原因是: 

    这使得两个不同的结构可以放到同一个链表中,尽管sk_buff_head要比sk_buff小巧的多。另外,相同的函数可以同样应用于sk_buff和sk_buff_head。 

    然后qlen域表示了当前的sk_buff链上包含多少个skb。 

    lock域是自旋锁。 2、sk_buff结构
      1 /** 
      2  *    struct sk_buff - socket buffer
      3  *    @next: Next buffer in list
      4  *    @prev: Previous buffer in list
      5  *    @sk: Socket we are owned by
      6  *    @tstamp: Time we arrived
      7  *    @dev: Device we arrived on/are leaving by
      8  *    @transport_header: Transport layer header
      9  *    @network_header: Network layer header
     10  *    @mac_header: Link layer header
     11  *    @_skb_refdst: destination entry (with norefcount bit)
     12  *    @sp: the security path, used for xfrm
     13  *    @cb: Control buffer. Free for use by every layer. Put private vars here
     14  *    @len: Length of actual data
     15  *    @data_len: Data length
     16  *    @mac_len: Length of link layer header
     17  *    @hdr_len: writable header length of cloned skb
     18  *    @csum: Checksum (must include start/offset pair)
     19  *    @csum_start: Offset from skb->head where checksumming should start
     20  *    @csum_offset: Offset from csum_start where checksum should be stored
     21  *    @local_df: allow local fragmentation
     22  *    @cloned: Head may be cloned (check refcnt to be sure)
     23  *    @nohdr: Payload reference only, must not modify header
     24  *    @pkt_type: Packet class
     25  *    @fclone: skbuff clone status
     26  *    @ip_summed: Driver fed us an IP checksum
     27  *    @priority: Packet queueing priority
     28  *    @users: User count - see {datagram,tcp}.c
     29  *    @protocol: Packet protocol from driver
     30  *    @truesize: Buffer size 
     31  *    @head: Head of buffer
     32  *    @data: Data head pointer
     33  *    @tail: Tail pointer
     34  *    @end: End pointer
     35  *    @destructor: Destruct function
     36  *    @mark: Generic packet mark
     37  *    @nfct: Associated connection, if any
     38  *    @ipvs_property: skbuff is owned by ipvs
     39  *    @peeked: this packet has been seen already, so stats have been
     40  *        done for it, don't do them again
     41  *    @nf_trace: netfilter packet trace flag
     42  *    @nfctinfo: Relationship of this skb to the connection
     43  *    @nfct_reasm: netfilter conntrack re-assembly pointer
     44  *    @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
     45  *    @skb_iif: ifindex of device we arrived on
     46  *    @rxhash: the packet hash computed on receive
     47  *    @queue_mapping: Queue mapping for multiqueue devices
     48  *    @tc_index: Traffic control index
     49  *    @tc_verd: traffic control verdict
     50  *    @ndisc_nodetype: router type (from link layer)
     51  *    @dma_cookie: a cookie to one of several possible DMA operations
     52  *        done by skb DMA functions
     53  *    @secmark: security marking
     54  *    @vlan_tci: vlan tag control information
     55  */
     56 
     57 struct sk_buff {
     58     /* These two members must be first. */
     59     struct sk_buff        *next;
     60     struct sk_buff        *prev;
     61 
     62     //表示这个skb被接收的时间。
     63     ktime_t            tstamp;
     64     //表示从属于那个socket,主要是被4层用到
     65     struct sock        *sk;
     66     /*这个表示一个网络设备,当skb为输出时它表示skb将要输出的设备,当接收时,它表示输入设备。
     67      * 要注意,这个设备有可能会是虚拟设备(在3层以上看来)
     68      */
     69     struct net_device    *dev;
     70 
     71     /*
     72      * This is the control buffer. It is free to use for every
     73      * layer. Please put your private variables there. If you
     74      * want to keep them across layers you have to do a skb_clone()
     75      * first. This is owned by whoever has the skb queued ATM.
     76      */
     77     char            cb[48] __aligned(8);
     78     ///这里其实应该是dst_entry类型,不知道为什么内核要改为ul。这个域主要用于路由子系统。
     79     //这个数据结构保存了一些路由相关信息 
     80     unsigned long        _skb_refdst;
     81 #ifdef CONFIG_XFRM
     82     struct    sec_path    *sp;
     83 #endif
     84     ///这个长度表示当前的skb中的数据的长度,这个长度即包括buf中的数据也包括切片的数据,
     85     //也就是保存在skb_shared_info中的数据。这个值是会随着从一层到另一层而改变的。下面我们会对比这几个长度的。
     86     unsigned int        len,
     87     ///这个长度只表示切片数据的长度,也就是skb_shared_info中的长度
     88                     data_len;
     89     //链路层头部的长度
     90     __u16            mac_len,
     91     //这个主要用于clone的时候,它表示clone的skb的头的长度
     92                 hdr_len;
     93     //接下来是校验相关的域
     94     union {
     95         __wsum        csum;
     96         struct {
     97             __u16    csum_start;
     98             __u16    csum_offset;
     99         };
    100     };
    101     __u32            priority;
    102     kmemcheck_bitfield_begin(flags1);
    103     //首先是是否可以本地切片的标志。
    104     __u8            local_df:1,
    105     //为1说明头可能已被clone
    106                 cloned:1,
    107     //这个表示校验相关的一个标记,表示硬件驱动是否为我们已经进行了校验
    108                 ip_summed:2,
    109     //这个域如果为1,则说明这个skb的头域指针已经分配完毕,因此这个时候计算头的长度只需要head和data的差就可以了。
    110                 nohdr:1,
    111                 nfctinfo:3;
    112     //pkt_type主要是表示数据包的类型,比如多播,单播,回环等等
    113     __u8            pkt_type:3,
    114     //这个域是一个clone标记。主要是在fast clone中被设置,我们后面讲到fast clone时会详细介绍这个域
    115                 fclone:2,
    116     //ipvs拥有的域
    117                 ipvs_property:1,
    118     //这个包已经被查看过了
    119                 peeked:1,
    120     //netfilter使用的域。是一个trace 标记
    121                 nf_trace:1;
    122     kmemcheck_bitfield_end(flags1);
    123     __be16            protocol;
    124     //skb的析构函数,一般都是设置为sock_rfree或者sock_wfree
    125     void            (*destructor)(struct sk_buff *skb);
    126 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
    127     struct nf_conntrack    *nfct;
    128 #endif
    129 #ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
    130     struct sk_buff        *nfct_reasm;
    131 #endif
    132 #ifdef CONFIG_BRIDGE_NETFILTER
    133     struct nf_bridge_info    *nf_bridge;
    134 #endif
    135     //接收设备的index
    136     int            skb_iif;
    137 
    138     //流量控制的相关域
    139 #ifdef CONFIG_NET_SCHED
    140     __u16            tc_index;    /* traffic control index */
    141 #ifdef CONFIG_NET_CLS_ACT
    142     __u16            tc_verd;    /* traffic control verdict */
    143 #endif
    144 #endif
    145 
    146     __u32            rxhash;
    147 
    148     kmemcheck_bitfield_begin(flags2);
    149     //多队列设备的映射,也就是说映射到那个队列
    150     __u16            queue_mapping:16;
    151 #ifdef CONFIG_IPV6_NDISC_NODETYPE
    152     __u8            ndisc_nodetype:2,
    153                 deliver_no_wcard:1;
    154 #else
    155     __u8            deliver_no_wcard:1;
    156 #endif
    157     __u8            ooo_okay:1;
    158     kmemcheck_bitfield_end(flags2);
    159 
    160     /* 0/13 bit hole */
    161 
    162 #ifdef CONFIG_NET_DMA
    163     dma_cookie_t        dma_cookie;
    164 #endif
    165 #ifdef CONFIG_NETWORK_SECMARK
    166     __u32            secmark;
    167 #endif
    168     union {
    169         //skb的标记
    170         __u32        mark;
    171         __u32        dropcount;
    172     };
    173     //vlan的控制tag
    174     __u16            vlan_tci;
    175     //传输层的头
    176     sk_buff_data_t        transport_header;
    177     //网络层的头
    178     sk_buff_data_t        network_header;
    179     //链路层的头
    180     sk_buff_data_t        mac_header;
    181     /* These elements must be at the end, see alloc_skb() for details.  */
    182     sk_buff_data_t        tail;
    183     sk_buff_data_t        end;
    184     unsigned char        *head,
    185                 *data;
    186     //这个表示整个skb的大小,包括skb本身,以及数据
    187     unsigned int        truesize;
    188     //skb的引用计数
    189     atomic_t        users;
    190 };

    【linux驱动分析】之dm9000驱动分析(四):net_device结构体

    net_device结构体,定义在include/linux/netdevice.h中,这是一个很复杂的结构体,先把代码清单列出来,再用到的过程中,逐步分析,最后来这里做个总结。
    下面的代码是linux-2.6.38中的。
      1 /*
      2  *    The DEVICE structure.
      3  *    Actually, this whole structure is a big mistake.  It mixes I/O
      4  *    data with strictly "high-level" data, and it has to know about
      5  *    almost every data structure used in the INET module.
      6  *
      7  *    FIXME: cleanup struct net_device such that network protocol info
      8  *    moves out.
      9  */
     10 
     11 struct net_device {
     12 
     13     /*
     14      * This is the first field of the "visible" part of this structure
     15      * (i.e. as seen by users in the "Space.c" file).  It is the name
     16      * of the interface.
     17      */
     18     char            name[IFNAMSIZ];
     19 
     20     struct pm_qos_request_list pm_qos_req;
     21 
     22     /* device name hash chain */
     23     struct hlist_node    name_hlist;
     24     /* snmp alias */
     25     char             *ifalias;
     26 
     27     /*
     28      *    I/O specific fields
     29      *    FIXME: Merge these and struct ifmap into one
     30      */
     31     unsigned long        mem_end;    /* shared mem end    */
     32     unsigned long        mem_start;    /* shared mem start    */
     33     unsigned long        base_addr;    /* device I/O address    */
     34     unsigned int        irq;        /* device IRQ number    */
     35 
     36     /*
     37      *    Some hardware also needs these fields, but they are not
     38      *    part of the usual set specified in Space.c.
     39      */
     40 
     41     unsigned char        if_port;    /* Selectable AUI, TP,..*/
     42     unsigned char        dma;        /* DMA channel        */
     43 
     44     unsigned long        state;
     45 
     46     struct list_head    dev_list;
     47     struct list_head    napi_list;
     48     struct list_head    unreg_list;
     49 
     50     /* Net device features */
     51     unsigned long        features;
     52 #define NETIF_F_SG        1    /* Scatter/gather IO. */
     53 #define NETIF_F_IP_CSUM        2    /* Can checksum TCP/UDP over IPv4. */
     54 #define NETIF_F_NO_CSUM        4    /* Does not require checksum. F.e. loopack. */
     55 #define NETIF_F_HW_CSUM        8    /* Can checksum all the packets. */
     56 #define NETIF_F_IPV6_CSUM    16    /* Can checksum TCP/UDP over IPV6 */
     57 #define NETIF_F_HIGHDMA        32    /* Can DMA to high memory. */
     58 #define NETIF_F_FRAGLIST    64    /* Scatter/gather IO. */
     59 #define NETIF_F_HW_VLAN_TX    128    /* Transmit VLAN hw acceleration */
     60 #define NETIF_F_HW_VLAN_RX    256    /* Receive VLAN hw acceleration */
     61 #define NETIF_F_HW_VLAN_FILTER    512    /* Receive filtering on VLAN */
     62 #define NETIF_F_VLAN_CHALLENGED    1024    /* Device cannot handle VLAN packets */
     63 #define NETIF_F_GSO        2048    /* Enable software GSO. */
     64 #define NETIF_F_LLTX        4096    /* LockLess TX - deprecated. Please */
     65                     /* do not use LLTX in new drivers */
     66 #define NETIF_F_NETNS_LOCAL    8192    /* Does not change network namespaces */
     67 #define NETIF_F_GRO        16384    /* Generic receive offload */
     68 #define NETIF_F_LRO        32768    /* large receive offload */
     69 
     70 /* the GSO_MASK reserves bits 16 through 23 */
     71 #define NETIF_F_FCOE_CRC    (1 << 24) /* FCoE CRC32 */
     72 #define NETIF_F_SCTP_CSUM    (1 << 25) /* SCTP checksum offload */
     73 #define NETIF_F_FCOE_MTU    (1 << 26) /* Supports max FCoE MTU, 2158 bytes*/
     74 #define NETIF_F_NTUPLE        (1 << 27) /* N-tuple filters supported */
     75 #define NETIF_F_RXHASH        (1 << 28) /* Receive hashing offload */
     76 
     77     /* Segmentation offload features */
     78 #define NETIF_F_GSO_SHIFT    16
     79 #define NETIF_F_GSO_MASK    0x00ff0000
     80 #define NETIF_F_TSO        (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)
     81 #define NETIF_F_UFO        (SKB_GSO_UDP << NETIF_F_GSO_SHIFT)
     82 #define NETIF_F_GSO_ROBUST    (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT)
     83 #define NETIF_F_TSO_ECN        (SKB_GSO_TCP_ECN << NETIF_F_GSO_SHIFT)
     84 #define NETIF_F_TSO6        (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT)
     85 #define NETIF_F_FSO        (SKB_GSO_FCOE << NETIF_F_GSO_SHIFT)
     86 
     87     /* List of features with software fallbacks. */
     88 #define NETIF_F_GSO_SOFTWARE    (NETIF_F_TSO | NETIF_F_TSO_ECN | \
     89                  NETIF_F_TSO6 | NETIF_F_UFO)
     90 
     91 
     92 #define NETIF_F_GEN_CSUM    (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
     93 #define NETIF_F_V4_CSUM        (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
     94 #define NETIF_F_V6_CSUM        (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
     95 #define NETIF_F_ALL_CSUM    (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
     96 
     97     /*
     98      * If one device supports one of these features, then enable them
     99      * for all in netdev_increment_features.
    100      */
    101 #define NETIF_F_ONE_FOR_ALL    (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | \
    102                  NETIF_F_SG | NETIF_F_HIGHDMA |        \
    103                  NETIF_F_FRAGLIST)
    104 
    105     /* Interface index. Unique device identifier    */
    106     int            ifindex;
    107     int            iflink;
    108 
    109     struct net_device_stats    stats;
    110     atomic_long_t        rx_dropped; /* dropped packets by core network
    111                          * Do not use this in drivers.
    112                          */
    113 
    114 #ifdef CONFIG_WIRELESS_EXT
    115     /* List of functions to handle Wireless Extensions (instead of ioctl).
    116      * See <net/iw_handler.h> for details. Jean II */
    117     const struct iw_handler_def *    wireless_handlers;
    118     /* Instance data managed by the core of Wireless Extensions. */
    119     struct iw_public_data *    wireless_data;
    120 #endif
    121     /* Management operations */
    122     const struct net_device_ops *netdev_ops;
    123     const struct ethtool_ops *ethtool_ops;
    124 
    125     /* Hardware header description */
    126     const struct header_ops *header_ops;
    127 
    128     unsigned int        flags;    /* interface flags (a la BSD)    */
    129     unsigned short        gflags;
    130         unsigned int            priv_flags; /* Like 'flags' but invisible to userspace. */
    131     unsigned short        padded;    /* How much padding added by alloc_netdev() */
    132 
    133     unsigned char        operstate; /* RFC2863 operstate */
    134     unsigned char        link_mode; /* mapping policy to operstate */
    135 
    136     unsigned int        mtu;    /* interface MTU value        */
    137     unsigned short        type;    /* interface hardware type    */
    138     unsigned short        hard_header_len;    /* hardware hdr length    */
    139 
    140     /* extra head- and tailroom the hardware may need, but not in all cases
    141      * can this be guaranteed, especially tailroom. Some cases also use
    142      * LL_MAX_HEADER instead to allocate the skb.
    143      */
    144     unsigned short        needed_headroom;
    145     unsigned short        needed_tailroom;
    146 
    147     /* Interface address info. */
    148     unsigned char        perm_addr[MAX_ADDR_LEN]; /* permanent hw address */
    149     unsigned char        addr_assign_type; /* hw address assignment type */
    150     unsigned char        addr_len;    /* hardware address length    */
    151     unsigned short          dev_id;        /* for shared network cards */
    152 
    153     spinlock_t        addr_list_lock;
    154     struct netdev_hw_addr_list    uc;    /* Unicast mac addresses */
    155     struct netdev_hw_addr_list    mc;    /* Multicast mac addresses */
    156     int            uc_promisc;
    157     unsigned int        promiscuity;
    158     unsigned int        allmulti;
    159 
    160 
    161     /* Protocol specific pointers */
    162 
    163 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
    164     struct vlan_group __rcu    *vlgrp;        /* VLAN group */
    165 #endif
    166 #ifdef CONFIG_NET_DSA
    167     void            *dsa_ptr;    /* dsa specific data */
    168 #endif
    169     void             *atalk_ptr;    /* AppleTalk link     */
    170     struct in_device __rcu    *ip_ptr;    /* IPv4 specific data    */
    171     struct dn_dev __rcu     *dn_ptr;        /* DECnet specific data */
    172     struct inet6_dev __rcu    *ip6_ptr;       /* IPv6 specific data */
    173     void            *ec_ptr;    /* Econet specific data    */
    174     void            *ax25_ptr;    /* AX.25 specific data */
    175     struct wireless_dev    *ieee80211_ptr;    /* IEEE 802.11 specific data,
    176                            assign before registering */
    177 
    178 /*
    179  * Cache lines mostly used on receive path (including eth_type_trans())
    180  */
    181     unsigned long        last_rx;    /* Time of last Rx
    182                          * This should not be set in
    183                          * drivers, unless really needed,
    184                          * because network stack (bonding)
    185                          * use it if/when necessary, to
    186                          * avoid dirtying this cache line.
    187                          */
    188 
    189     struct net_device    *master; /* Pointer to master device of a group,
    190                       * which this device is member of.
    191                       */
    192 
    193     /* Interface address info used in eth_type_trans() */
    194     unsigned char        *dev_addr;    /* hw address, (before bcast
    195                            because most packets are
    196                            unicast) */
    197 
    198     struct netdev_hw_addr_list    dev_addrs; /* list of device
    199                               hw addresses */
    200 
    201     unsigned char        broadcast[MAX_ADDR_LEN];    /* hw bcast add    */
    202 
    203 #ifdef CONFIG_RPS
    204     struct kset        *queues_kset;
    205 
    206     struct netdev_rx_queue    *_rx;
    207 
    208     /* Number of RX queues allocated at register_netdev() time */
    209     unsigned int        num_rx_queues;
    210 
    211     /* Number of RX queues currently active in device */
    212     unsigned int        real_num_rx_queues;
    213 #endif
    214 
    215     rx_handler_func_t __rcu    *rx_handler;
    216     void __rcu        *rx_handler_data;
    217 
    218     struct netdev_queue __rcu *ingress_queue;
    219 
    220 /*
    221  * Cache lines mostly used on transmit path
    222  */
    223     struct netdev_queue    *_tx ____cacheline_aligned_in_smp;
    224 
    225     /* Number of TX queues allocated at alloc_netdev_mq() time  */
    226     unsigned int        num_tx_queues;
    227 
    228     /* Number of TX queues currently active in device  */
    229     unsigned int        real_num_tx_queues;
    230 
    231     /* root qdisc from userspace point of view */
    232     struct Qdisc        *qdisc;
    233 
    234     unsigned long        tx_queue_len;    /* Max frames per queue allowed */
    235     spinlock_t        tx_global_lock;
    236 
    237 #ifdef CONFIG_XPS
    238     struct xps_dev_maps __rcu *xps_maps;
    239 #endif
    240 
    241     /* These may be needed for future network-power-down code. */
    242 
    243     /*
    244      * trans_start here is expensive for high speed devices on SMP,
    245      * please use netdev_queue->trans_start instead.
    246      */
    247     unsigned long        trans_start;    /* Time (in jiffies) of last Tx    */
    248 
    249     int            watchdog_timeo; /* used by dev_watchdog() */
    250     struct timer_list    watchdog_timer;
    251 
    252     /* Number of references to this device */
    253     int __percpu        *pcpu_refcnt;
    254 
    255     /* delayed register/unregister */
    256     struct list_head    todo_list;
    257     /* device index hash chain */
    258     struct hlist_node    index_hlist;
    259 
    260     struct list_head    link_watch_list;
    261 
    262     /* register/unregister state machine */
    263     enum { NETREG_UNINITIALIZED=0,
    264            NETREG_REGISTERED,    /* completed register_netdevice */
    265            NETREG_UNREGISTERING,    /* called unregister_netdevice */
    266            NETREG_UNREGISTERED,    /* completed unregister todo */
    267            NETREG_RELEASED,        /* called free_netdev */
    268            NETREG_DUMMY,        /* dummy device for NAPI poll */
    269     } reg_state:16;
    270 
    271     enum {
    272         RTNL_LINK_INITIALIZED,
    273         RTNL_LINK_INITIALIZING,
    274     } rtnl_link_state:16;
    275 
    276     /* Called from unregister, can be used to call free_netdev */
    277     void (*destructor)(struct net_device *dev);
    278 
    279 #ifdef CONFIG_NETPOLL
    280     struct netpoll_info    *npinfo;
    281 #endif
    282 
    283 #ifdef CONFIG_NET_NS
    284     /* Network namespace this network device is inside */
    285     struct net        *nd_net;
    286 #endif
    287 
    288     /* mid-layer private */
    289     union {
    290         void                *ml_priv;
    291         struct pcpu_lstats __percpu    *lstats; /* loopback stats */
    292         struct pcpu_tstats __percpu    *tstats; /* tunnel stats */
    293         struct pcpu_dstats __percpu    *dstats; /* dummy stats */
    294     };
    295     /* GARP */
    296     struct garp_port __rcu    *garp_port;
    297 
    298     /* class/net/name entry */
    299     struct device        dev;
    300     /* space for optional device, statistics, and wireless sysfs groups */
    301     const struct attribute_group *sysfs_groups[4];
    302 
    303     /* rtnetlink link ops */
    304     const struct rtnl_link_ops *rtnl_link_ops;
    305 
    306     /* VLAN feature mask */
    307     unsigned long vlan_features;
    308 
    309     /* for setting kernel sock attribute on TCP connection setup */
    310 #define GSO_MAX_SIZE        65536
    311     unsigned int        gso_max_size;
    312 
    313 #ifdef CONFIG_DCB
    314     /* Data Center Bridging netlink ops */
    315     const struct dcbnl_rtnl_ops *dcbnl_ops;
    316 #endif
    317 
    318 #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
    319     /* max exchange id for FCoE LRO by ddp */
    320     unsigned int        fcoe_ddp_xid;
    321 #endif
    322     /* n-tuple filter list attached to this device */
    323     struct ethtool_rx_ntuple_list ethtool_ntuple_list;
    324 
    325     /* phy device may attach itself for hardware timestamping */
    326     struct phy_device *phydev;
    327 };



    【linux驱动分析】之dm9000驱动分析(五):另外几个重要的结构体


    除了sk_buff和net_device,dm9000驱动中用到的另外几个重要的结构体

    一、platform_driver
    定义在include/linux/platform_device.h中,代码如下:
    1 struct platform_driver {
    2     int (*probe)(struct platform_device *);
    3     int (*remove)(struct platform_device *);
    4     void (*shutdown)(struct platform_device *);
    5     int (*suspend)(struct platform_device *, pm_message_t state);
    6     int (*resume)(struct platform_device *);
    7     struct device_driver driver;
    8     const struct platform_device_id *id_table;
    9 };
    dm9000.c中的初始化:
    1 static struct platform_driver dm9000_driver = {
    2     .driver    = {
    3         .name    = "dm9000",
    4         .owner     = THIS_MODULE,
    5         .pm     = &dm9000_drv_pm_ops,
    6     },
    7     .probe   = dm9000_probe,
    8     .remove  = __devexit_p(dm9000_drv_remove),
    9 };
    二、net_device_ops
    操作网络设备的函数,定义在include/linux/netdevice.h中,它有很多成员,这里只列出dm9000驱动用到的:
     1 static const struct net_device_ops dm9000_netdev_ops = {
     2     .ndo_open        = dm9000_open,
     3     .ndo_stop        = dm9000_stop,
     4     .ndo_start_xmit        = dm9000_start_xmit,
     5     .ndo_tx_timeout        = dm9000_timeout,
     6     .ndo_set_multicast_list    = dm9000_hash_table,
     7     .ndo_do_ioctl        = dm9000_ioctl,
     8     .ndo_change_mtu        = eth_change_mtu,
     9     .ndo_validate_addr    = eth_validate_addr,
    10     .ndo_set_mac_address    = eth_mac_addr,
    11 #ifdef CONFIG_NET_POLL_CONTROLLER
    12     .ndo_poll_controller    = dm9000_poll_controller,
    13 #endif
    14 };

    三、ethtool_ops
    ethtool_ops定义在include/linux/ethtool.h中,它有很多成员,这里只列出dm9000驱动用到的:
     1 static const struct ethtool_ops dm9000_ethtool_ops = {
     2     .get_drvinfo        = dm9000_get_drvinfo,
     3     .get_settings        = dm9000_get_settings,
     4     .set_settings        = dm9000_set_settings,
     5     .get_msglevel        = dm9000_get_msglevel,
     6     .set_msglevel        = dm9000_set_msglevel,
     7     .nway_reset        = dm9000_nway_reset,
     8     .get_link        = dm9000_get_link,
     9     .get_wol        = dm9000_get_wol,
    10     .set_wol        = dm9000_set_wol,
    11      .get_eeprom_len        = dm9000_get_eeprom_len,
    12      .get_eeprom        = dm9000_get_eeprom,
    13      .set_eeprom        = dm9000_set_eeprom,
    14     .get_rx_csum        = dm9000_get_rx_csum,
    15     .set_rx_csum        = dm9000_set_rx_csum,
    16     .get_tx_csum        = ethtool_op_get_tx_csum,
    17     .set_tx_csum        = dm9000_set_tx_csum,
    18 };
    四、board_info
    用来保存芯片相关的一些信息。
     1 /* Structure/enum declaration ------------------------------- */
     2 typedef struct board_info {
     3 
     4     void __iomem    *io_addr;    /* Register I/O base address */
     5     void __iomem    *io_data;    /* Data I/O address */
     6     u16         irq;        /* IRQ */
     7 
     8     u16        tx_pkt_cnt;
     9     u16        queue_pkt_len;
    10     u16        queue_start_addr;
    11     u16        queue_ip_summed;
    12     u16        dbug_cnt;
    13     u8        io_mode;        /* 0:word, 2:byte */
    14     u8        phy_addr;
    15     u8        imr_all;
    16 
    17     unsigned int    flags;
    18     unsigned int    in_suspend :1;
    19     unsigned int    wake_supported :1;
    20     int        debug_level;
    21 
    22     enum dm9000_type type;
    23 
    24     void (*inblk)(void __iomem *port, void *data, int length);
    25     void (*outblk)(void __iomem *port, void *data, int length);
    26     void (*dumpblk)(void __iomem *port, int length);
    27 
    28     struct device    *dev;         /* parent device */
    29 
    30     struct resource    *addr_res;   /* resources found */
    31     struct resource *data_res;
    32     struct resource    *addr_req;   /* resources requested */
    33     struct resource *data_req;
    34     struct resource *irq_res;
    35 
    36     int         irq_wake;
    37 
    38     struct mutex     addr_lock;    /* phy and eeprom access lock */
    39 
    40     struct delayed_work phy_poll;
    41     struct net_device  *ndev;
    42 
    43     spinlock_t    lock;
    44 
    45     struct mii_if_info mii;
    46     u32        msg_enable;
    47     u32        wake_state;
    48 
    49     int        rx_csum;
    50     int        can_csum;
    51     int        ip_summed;
    52 } board_info_t;


    【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现


    一、dm9000_init
    打印出驱动的版本号,注册dm9000_driver驱动,将驱动添加到总线上,执行match,如果匹配,将会执行probe函数。
    1 static int __init
    2 dm9000_init(void)
    3 {
    4     printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);
    5 
    6     return platform_driver_register(&dm9000_driver);
    7 }

    二、dm9000_probe函数
      1 /*
      2  * Search DM9000 board, allocate space and register it
      3  */
      4 static int __devinit
      5 dm9000_probe(struct platform_device *pdev)
      6 {
      7     /* 把mach-mini6410.c中定义的dm9000_plat_data传递过来 */
      8     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
      9     struct board_info *db;    /* Point a board information structure */
     10     struct net_device *ndev;
     11     const unsigned char *mac_src;
     12     int ret = 0;
     13     int iosize;
     14     int i;
     15     u32 id_val;
     16 
     17     /* Init network device */
     18     /* 分配一个名为eth%d的网络设备,同时分配一个私有数据区,数据区是32字节对齐 */
     19     ndev = alloc_etherdev(sizeof(struct board_info));
     20     if (!ndev) {
     21         dev_err(&pdev->dev, "could not allocate device.\n");
     22         return -ENOMEM;
     23     }
     24     /* 把网络设备的基类dev的父指针设为平台设备的基类dev */
     25     SET_NETDEV_DEV(ndev, &pdev->dev);
     26 
     27     dev_dbg(&pdev->dev, "dm9000_probe()\n");
     28 
     29     /* setup board info structure */
     30     /* 设置私有数据,下面会具体分析这个函数 */
     31     db = netdev_priv(ndev);
     32     /* 给私有数据赋值 */
     33     db->dev = &pdev->dev;
     34     db->ndev = ndev;
     35     
     36     /* 初始化一个自旋锁和一个互斥体 */
     37     spin_lock_init(&db->lock);
     38     mutex_init(&db->addr_lock);
     39     
     40     /* 往工作队列插入一个工作,随后我们调用schedule_delayed_work就会执行传递的函数
     41      * 关于工作队列会有专门一篇文章来学习总结
     42      */
     43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
     44 
     45     /* 获得资源,这个函数会在下面讲解 */
     46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
     48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
     49 
     50     if (db->addr_res == NULL || db->data_res == NULL ||
     51         db->irq_res == NULL) {
     52         dev_err(db->dev, "insufficient resources\n");
     53         ret = -ENOENT;
     54         goto out;
     55     }
     56     
     57     /* 获取中断号,这个中断号是不存在的,因为resource里只有一个中断号 */
     58     db->irq_wake = platform_get_irq(pdev, 1);
     59     if (db->irq_wake >= 0) {
     60         dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);
     61         
     62         /* 为ndev申请中断,中断服务程序为dm9000_wol_interrupt,关于中断也会有一篇文章来学习总结 */
     63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
     64                   IRQF_SHARED, dev_name(db->dev), ndev);
     65         if (ret) {
     66             dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);
     67         } else {
     68 
     69             /* test to see if irq is really wakeup capable */
     70             ret = set_irq_wake(db->irq_wake, 1);
     71             if (ret) {
     72                 dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",
     73                     db->irq_wake, ret);
     74                 ret = 0;
     75             } else {
     76                 set_irq_wake(db->irq_wake, 0);
     77                 db->wake_supported = 1;
     78             }
     79         }
     80     }
     81     /* 返回dm9000内存资源的大小,下面一句是申请内存,关于内存的申请和分配也会有一篇文章 */
     82     iosize = resource_size(db->addr_res);
     83     db->addr_req = request_mem_region(db->addr_res->start, iosize,
     84                       pdev->name);
     85 
     86     if (db->addr_req == NULL) {
     87         dev_err(db->dev, "cannot claim address reg area\n");
     88         ret = -EIO;
     89         goto out;
     90     }
     91     /* 存放地址的内存空间开始地址,地址寄存器,一共占3个地址,
     92      * 分别是0x18000000,0x18000001,0x18000002,0x18000003,
     93      * 这也是一个巧妙之处,dm9000芯片的cmd引脚接的是arm11的addr2,
     94      * 所以写“0地址”代表送地址,读写4地址表示读写数据
     95      * */
     96     db->io_addr = ioremap(db->addr_res->start, iosize);
     97 
     98     if (db->io_addr == NULL) {
     99         dev_err(db->dev, "failed to ioremap address reg\n");
    100         ret = -EINVAL;
    101         goto out;
    102     }
    103 
    104     iosize = resource_size(db->data_res);
    105     db->data_req = request_mem_region(db->data_res->start, iosize,
    106                       pdev->name);
    107 
    108     if (db->data_req == NULL) {
    109         dev_err(db->dev, "cannot claim data reg area\n");
    110         ret = -EIO;
    111         goto out;
    112     }
    113     /* 数据寄存器的地址,1MB的空间 */
    114     db->io_data = ioremap(db->data_res->start, iosize);
    115 
    116     if (db->io_data == NULL) {
    117         dev_err(db->dev, "failed to ioremap data reg\n");
    118         ret = -EINVAL;
    119         goto out;
    120     }
    121 
    122     /* fill in parameters for net-dev structure */
    123     ndev->base_addr = (unsigned long)db->io_addr;
    124     ndev->irq    = db->irq_res->start;
    125 
    126     /* ensure at least we have a default set of IO routines */
    127     /* iosize是一个很大的值,这里是先保证有个默认值位宽,32位 */
    128     dm9000_set_io(db, iosize);
    129 
    130     /* check to see if anything is being over-ridden */
    131     if (pdata != NULL) {
    132         /* check to see if the driver wants to over-ride the
    133          * default IO width */
    134 
    135         if (pdata->flags & DM9000_PLATF_8BITONLY)
    136             dm9000_set_io(db, 1);
    137         /*
    138          * 我们这里设置的是16位,他会做下面几件事:
    139          * db->dumpblk = dm9000_dumpblk_16bit;
    140          * db->outblk  = dm9000_outblk_16bit;
    141          * db->inblk   = dm9000_inblk_16bit;
    142          */
    143         if (pdata->flags & DM9000_PLATF_16BITONLY)
    144             dm9000_set_io(db, 2);
    145 
    146         if (pdata->flags & DM9000_PLATF_32BITONLY)
    147             dm9000_set_io(db, 4);
    148 
    149         /* check to see if there are any IO routine
    150          * over-rides */
    151 
    152         if (pdata->inblk != NULL)
    153             db->inblk = pdata->inblk;
    154 
    155         if (pdata->outblk != NULL)
    156             db->outblk = pdata->outblk;
    157 
    158         if (pdata->dumpblk != NULL)
    159             db->dumpblk = pdata->dumpblk;
    160 
    161         db->flags = pdata->flags;
    162     }
    163 
    164 #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
    165     db->flags |= DM9000_PLATF_SIMPLE_PHY;
    166 #endif
    167     /*
    168      * dm9000_reset函数是执行下面两句话,中间有延时,这里省略了
    169      * writeb(DM9000_NCR, db->io_addr); //先写NCR寄存器的地址到“0地址”(写到0地址就代表写地址)
    170      * writeb(NCR_RST, db->io_data);    //再给“4地址”写NCR_RST(0x01),即NCR = 1;
    171      * 读写“4地址”就相当于发送数据,cmd引脚连的是addr2,dm9000地址线数据线复用
    172      * */
    173     dm9000_reset(db);
    174 
    175     /* try multiple times, DM9000 sometimes gets the read wrong */
    176     /* 下面所做的是读出dm9000的供应商ID和产品ID */
    177     for (i = 0; i < 8; i++) {
    178         /* ior是从reg读出数据,类型是u8,它的原理与上面分析reset函数的原理是一样的 */
    179         id_val  = ior(db, DM9000_VIDL);
    180         id_val |= (u32)ior(db, DM9000_VIDH) << 8;
    181         id_val |= (u32)ior(db, DM9000_PIDL) << 16;
    182         id_val |= (u32)ior(db, DM9000_PIDH) << 24;
    183 
    184         if (id_val == DM9000_ID)
    185             break;
    186         dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
    187     }
    188 
    189     if (id_val != DM9000_ID) {
    190         dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
    191         ret = -ENODEV;
    192         goto out;
    193     }
    194 
    195     /* Identify what type of DM9000 we are working on */
    196     /* 读出芯片版本寄存器,判断dm9000的型号,默认是dm9000E */
    197     id_val = ior(db, DM9000_CHIPR);
    198     dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);
    199 
    200     switch (id_val) {
    201     case CHIPR_DM9000A:
    202         db->type = TYPE_DM9000A;
    203         break;
    204     case CHIPR_DM9000B:
    205         db->type = TYPE_DM9000B;
    206         break;
    207     default:
    208         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);
    209         db->type = TYPE_DM9000E;
    210     }
    211 
    212     /* dm9000a/b are capable of hardware checksum offload */
    213     if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
    214         db->can_csum = 1;
    215         db->rx_csum = 1;
    216         ndev->features |= NETIF_F_IP_CSUM;
    217     }
    218 
    219     /* from this point we assume that we have found a DM9000 */
    220 
    221     /* driver system function */
    222     /* 这个函数是初始化ndev的一些成员 */
    223     ether_setup(ndev);
    224 
    225     /* 下面也是初始化ndev的一些成员 */
    226     ndev->netdev_ops    = &dm9000_netdev_ops;
    227     ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);
    228     ndev->ethtool_ops    = &dm9000_ethtool_ops;
    229 
    230     db->msg_enable       = NETIF_MSG_LINK;
    231     db->mii.phy_id_mask  = 0x1f;
    232     db->mii.reg_num_mask = 0x1f;
    233     db->mii.force_media  = 0;
    234     db->mii.full_duplex  = 0;
    235     db->mii.dev         = ndev;
    236     db->mii.mdio_read    = dm9000_phy_read;
    237     db->mii.mdio_write   = dm9000_phy_write;
    238 
    239     mac_src = "eeprom";
    240     /* node address是在网络中的一个电脑或终端的号码或名字,
    241      * 这里从eeprom读取,由于我们没有,所以它读回来的是6个FF
    242      * */
    243     /* try reading the node address from the attached EEPROM */
    244     for (i = 0; i < 6; i += 2)
    245         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
    246 
    247     /* try MAC address passed by kernel command line */
    248     /* 这个函数是友善之臂添加的,它在mach-mini6410里添加了这样一句话__setup("ethmac=", dm9000_set_mac);
    249      * 内核启动时,遇到"ethmac="回去执行dm9000_set_mac函数,所以就实现了mac从内核传递过来
    250      * 这也是一个很巧妙的设计,需要写一篇文章学习总结一下
    251      * */
    252     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
    253         mac_src = "param data";
    254         memcpy(ndev->dev_addr, pdata->param_addr, 6);
    255     }
    256     /* 下面是读取mac的几种方法,当前这一种是从dm9000的Physical Address Register读取 */
    257     if (!is_valid_ether_addr(ndev->dev_addr)) {
    258         /* try reading from mac */
    259         mac_src = "chip";
    260         for (i = 0; i < 6; i++)
    261             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
    262     }
    263     /* 从pdata里的dev_addr读取 */
    264     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
    265         mac_src = "platform data";
    266         memcpy(ndev->dev_addr, pdata->dev_addr, 6);
    267     }
    268     /* 没有读到有效的mac地址,提示用ifconfig命令设置 */
    269     if (!is_valid_ether_addr(ndev->dev_addr))
    270         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
    271              "set using ifconfig\n", ndev->name);
    272 
    273     /* 
    274      * 这里由于ndev是我们定义的一个局部变量,所以要ndev传递给平台设备pdev
    275      * 即pdev->dev->p->driver_data = ndev;
    276      * 要使用是通过platform_get_drvdata获得
    277      * */
    278     platform_set_drvdata(pdev, ndev);
    279     /* net_device结构体初始化好后,剩余的工作就是把该结构传递给register_netdev函数,
    280      * 当调用register_netdev后就可以用驱动程序操作设备了,所以
    281      * 必须在初始化一切事情后再注册
    282      * */
    283     ret = register_netdev(ndev);
    284 
    285     if (ret == 0)
    286         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
    287                ndev->name, dm9000_type_to_char(db->type),
    288                db->io_addr, db->io_data, ndev->irq,
    289                ndev->dev_addr, mac_src);
    290     return 0;
    291 
    292 out:
    293     dev_err(db->dev, "not found (%d).\n", ret);
    294 
    295     dm9000_release_board(pdev, db);
    296     free_netdev(ndev);
    297 
    298     return ret;
    299 }
    300 /*********** probe函数大功告成 *************/
    301 
    

    三、总结probe函数分析是留下的问题
    在上面用红色标记出来了要分析的东西
    1、分析netdev_priv
    在执行 ndev = alloc_etherdev(sizeof(struct board_info));时,先分配了一个net_device结构,又分配了一个board_info结构体,作为ndev的私有数据,然后执行了db = netdev_priv( ndev );来获得私有数据的开始地址,并在以后做初始化。
    在probe函数最后,通过platform_set_drvdata函数把ndev结构传给了平台设备,以供使用,那么我猜想这里把board_info也传过去了,以后也可以用,获得它的地址的方法是通过下面的函数。
    303 /**
    304  *    netdev_priv - access network device private data
    305  *    @dev: network device
    306  *
    307  * Get network device private data
    308  */
    309 static inline void *netdev_priv(const struct net_device *dev)
    310 {
    311     return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);
    312 }

    2、platform_get_resource函数分析
    46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    platform_data的关系是这样的:platform device->dev->platform_data
    对于dm9000驱动来说是这样实现的:
    8     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
    static struct dm9000_plat_data dm9000_setup = {
    	.flags		= DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY,
    	.dev_addr       = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
    }; 

    3、以后要写文章总结的东西
    (1)、关于工作队列的要总结一下
    43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);

    (2)、关于内核中断的原理和操作方法
    63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
    64                   IRQF_SHARED, dev_name(db->dev), ndev);

    (3)、关于内核内存分配和操作方法
    83     db->addr_req = request_mem_region(db->addr_res->start, iosize,
    84                       pdev->name);
    (4)、关于__setup的作用
    __setup("ethmac=", dm9000_set_mac);



    【linux驱动分析】之dm9000驱动分析(七):dm9000的卸载挂起和恢复以及打开和停止


    分析dm9000的卸载,挂起和恢复,以及dm9000的打开和停止。涉及到的函数为:
     1 static int __devexit
     2   dm9000_drv_remove(struct platform_device *pdev)
     3 static int
     4  dm9000_drv_suspend(struct device *dev)
     5 static int
     6  dm9000_drv_resume(struct device *dev)
     7 static int
     8  dm9000_open(struct net_device *dev)
     9 static int
    10  dm9000_stop(struct net_device *ndev)
    一、卸载驱动
    驱动中可以看到,在模块卸载时执行
    platform_driver_unregister(&dm9000_driver);
    
    在卸载platform_driver时会执行remove函数,remove函数的功能是把设备从内核中移除,释放内存区域。下面给出dm9000_drv_remove函数的代码:
     1 static int __devexit
     2 dm9000_drv_remove(struct platform_device *pdev)
     3 {
     4     struct net_device *ndev = platform_get_drvdata(pdev);
     5 
     6     platform_set_drvdata(pdev, NULL);
     7 
     8     unregister_netdev(ndev);
     9     dm9000_release_board(pdev, netdev_priv(ndev));
    10     free_netdev(ndev);        /* free device structure */
    11 
    12     dev_dbg(&pdev->dev, "released and freed device\n");
    13     return 0;
    14 }
    15 
    16 
    17 /* dm9000_release_board
    18  *
    19  * release a board, and any mapped resources
    20  */
    21 
    22 static void
    23 dm9000_release_board(struct platform_device *pdev, struct board_info *db)
    24 {
    25     /* unmap our resources */
    26 
    27     iounmap(db->io_addr);
    28     iounmap(db->io_data);
    29 
    30     /* release the resources */
    31 
    32     release_resource(db->data_req);
    33     kfree(db->data_req);
    34 
    35     release_resource(db->addr_req);
    36     kfree(db->addr_req);
    37 }
    二、关于电源管理的设备的挂起和恢复函数

    suspend函数并不真正把设备从内核中移除,而只是标志设备为removed状态,并设置挂起标志位为1,最后关闭设备。

    resume函数将挂起的设备复位并初始化,软后将设备标志为attached状态,并设置挂起标志位为0。

     1 static int
     2 dm9000_drv_suspend(struct device *dev)
     3 {
     4     struct platform_device *pdev = to_platform_device(dev);
     5     struct net_device *ndev = platform_get_drvdata(pdev);
     6     board_info_t *db;
     7 
     8     if (ndev) {
     9         db = netdev_priv(ndev);
    10         db->in_suspend = 1;
    11 
    12         if (!netif_running(ndev))
    13             return 0;
    14 
    15         netif_device_detach(ndev);
    16 
    17         /* only shutdown if not using WoL */
    18         if (!db->wake_state)
    19             dm9000_shutdown(ndev);
    20     }
    21     return 0;
    22 }
    23 
    24 static int
    25 dm9000_drv_resume(struct device *dev)
    26 {
    27     struct platform_device *pdev = to_platform_device(dev);
    28     struct net_device *ndev = platform_get_drvdata(pdev);
    29     board_info_t *db = netdev_priv(ndev);
    30 
    31     if (ndev) {
    32         if (netif_running(ndev)) {
    33             /* reset if we were not in wake mode to ensure if
    34              * the device was powered off it is in a known state */
    35             if (!db->wake_state) {
    36                 dm9000_reset(db);
    37                 dm9000_init_dm9000(ndev);
    38             }
    39 
    40             netif_device_attach(ndev);
    41         }
    42 
    43         db->in_suspend = 0;
    44     }
    45     return 0;
    46 }
    三、dm9000的打开和停止

    1、打开函数dm9000_open
     1 /*
     2  *  Open the interface.
     3  *  The interface is opened whenever "ifconfig" actives it.
     4  */
     5 static int
     6 dm9000_open(struct net_device *dev)
     7 {
     8     board_info_t *db = netdev_priv(dev);
     9     unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
    10 
    11     /* db结构体中的成员msg_enble,在probe函数中赋值为NETIF_MSG_LINK */
    12     if (netif_msg_ifup(db))
    13         dev_dbg(db->dev, "enabling %s\n", dev->name);
    14 
    15     /* If there is no IRQ type specified, default to something that
    16      * may work, and tell the user that this is a problem */
    17 
    18     if (irqflags == IRQF_TRIGGER_NONE)
    19         dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
    20 
    21     irqflags |= IRQF_SHARED;
    22     /* 申请中断,中断函数dm9000_interrupt */
    23     if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
    24         return -EAGAIN;
    25 
    26     /* GPIO0 on pre-activate PHY, Reg 1F is not set by reset */
    27     iow(db, DM9000_GPR, 0);    /* REG_1F bit0 activate phyxcer */
    28     mdelay(1); /* delay needs by DM9000B */
    29 
    30     /* Initialize DM9000 board */
    31     dm9000_reset(db);
    32     /* dm9000初始化,下面会对这个函数做详细分析 */
    33     dm9000_init_dm9000(dev);
    34 
    35     /* Init driver variable */
    36     db->dbug_cnt = 0;
    37 
    38     /* 检查mii接口 
    39      * Returns 1 if the duplex mode changed, 0 if not.
    40      * If the media type is forced, always returns 0.
    41      * */
    42     mii_check_media(&db->mii, netif_msg_link(db), 1);
    43     /* 开启网络接口数据发送队列 */
    44     netif_start_queue(dev);
    45     /*延时一段时间执行dm9000_poll_work,原来在probe函数里把这个函数加入了工作队列,现在来调度执行*/
    46     dm9000_schedule_poll(db);
    47 
    48     return 0;
    49 }
    2、dm9000_init_dm9000函数
     1 /*
     2  * Initialize dm9000 board
     3  */
     4 static void
     5 dm9000_init_dm9000(struct net_device *dev)
     6 {
     7     board_info_t *db = netdev_priv(dev);
     8     unsigned int imr;
     9     unsigned int ncr;
    10 
    11     dm9000_dbg(db, 1, "entering %s\n", __func__);
    12 
    13     /* I/O mode */
    14     db->io_mode = ior(db, DM9000_ISR) >> 6;    /* ISR bit7:6 keeps I/O mode */
    15 
    16     /* Checksum mode */
    17     dm9000_set_rx_csum_unlocked(dev, db->rx_csum);
    18 
    19     iow(db, DM9000_GPCR, GPCR_GEP_CNTL);    /* Let GPIO0 output */
    20 
    21     ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
    22 
    23     /* if wol is needed, then always set NCR_WAKEEN otherwise we end
    24      * up dumping the wake events if we disable this. There is already
    25      * a wake-mask in DM9000_WCR */
    26     if (db->wake_supported)
    27         ncr |= NCR_WAKEEN;
    28 
    29     iow(db, DM9000_NCR, ncr);
    30 
    31     /* Program operating register */
    32     iow(db, DM9000_TCR, 0);            /* TX Polling clear */
    33     iow(db, DM9000_BPTR, 0x3f);    /* Less 3Kb, 200us */
    34     iow(db, DM9000_FCR, 0xff);    /* Flow Control */
    35     iow(db, DM9000_SMCR, 0);        /* Special Mode */
    36     /* clear TX status */
    37     iow(db, DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
    38     iow(db, DM9000_ISR, ISR_CLR_STATUS); /* Clear interrupt status */
    39 
    40     /* Set address filter table */
    41     dm9000_hash_table_unlocked(dev);
    42 
    43     imr = IMR_PAR | IMR_PTM | IMR_PRM;
    44     if (db->type != TYPE_DM9000E)
    45         imr |= IMR_LNKCHNG;
    46 
    47     db->imr_all = imr;
    48 
    49     /* Enable TX/RX interrupt mask */
    50     iow(db, DM9000_IMR, imr);
    51 
    52     /* Init Driver variable */
    53     db->tx_pkt_cnt = 0;
    54     db->queue_pkt_len = 0;
    55     dev->trans_start = jiffies;
    56 }
    3、停止函数dm9000_stop
    它会做与dm9000_stop相反的事情。
     1 /*
     2  * Stop the interface.
     3  * The interface is stopped when it is brought.
     4  */
     5 static int
     6 dm9000_stop(struct net_device *ndev)
     7 {
     8     board_info_t *db = netdev_priv(ndev);
     9 
    10     if (netif_msg_ifdown(db))
    11         dev_dbg(db->dev, "shutting down %s\n", ndev->name);
    12 
    13     cancel_delayed_work_sync(&db->phy_poll);
    14 
    15     netif_stop_queue(ndev);
    16     netif_carrier_off(ndev);
    17 
    18     /* free interrupt */
    19     free_irq(ndev->irq, ndev);
    20 
    21     dm9000_shutdown(ndev);
    22 
    23     return 0;
    24 }


    展开全文
  • linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析  【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏  【linux驱动分析】之dm9000驱动分析(三):sk_buff...

    【linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析 

    【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏 

    【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析 

    【linux驱动分析】之dm9000驱动分析(四):net_device结构体 

    【linux驱动分析】之dm9000驱动分析(五):另外几个重要的结构体 

    【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 

    【linux驱动分析】之dm9000驱动分析(七):dm9000的卸载挂起和恢复以及打开和停止


    一、了解几个概念
    1、MAC
        MAC是Media Access Control 的缩写,即媒体访问控制子层协议,又翻译为媒体接入控制器。
        该协议位于OSI七层协议中数据链路层的下半部分,主要负责控制与连接物理层的物理介质。在发送数据的时候,MAC协议可以事先判断是否可以发送数据,如果可以发送将给数据加上一些控制信息,最终将数据以及控制信息以规定的格式发送到物理层;在接收数据的时候,MAC协议首先判断输入的信息并是否发生传输错误,如果没有错误,则去掉控制信息发送至LLC层。以太网MAC由IEEE-802.3以太网标准定义。
    2、MII
        MII即媒体独立接口, “媒体独立”表明在不对MAC硬件重新设计或替换的情况下,任何类型的PHY设备都可以正常工作。包括分别用于发送器和接收器的两条独立信道。每条信道都有自己的数据、时钟和控制信号。MII数据接口总共需要16个信号,包括TX_ER,TXD<3:0>,TX_EN,TX_CLK,COL,RXD<3:0>,RX_EX,RX_CLK,CRS,RX_DV等。
        MII以4位半字节方式传送数据双向传输,时钟速率25MHz。其工作速率可达100Mb/s。MII管理接口是个双信号接口,一个是时钟信号,另一个是数据信号。通过管理接口,上层能监视和控制PHY。其管理是使用SMI(Serial Management Interface)总线通过读写PHY的寄存器来完成的。PHY里面的部分寄存器是IEEE定义的,这样PHY把自己的目前的状态反映到寄存器里面,MAC通过SMI总线不断的读取PHY的状态寄存器以得知目前PHY的状态,例如连接速度,双工的能力等。当然也可以通过SMI设置PHY的寄存器达到控制的目的,例如流控的打开关闭,自协商模式还是强制模式等。不论是物理连接的MII总线和SMI总线还是PHY的状态寄存器和控制寄存器都是有IEEE的规范的,因此不同公司的MAC和PHY一样可以协调工作。当然为了配合不同公司的PHY的自己特有的一些功能,驱动需要做相应的修改。

    3、PHY
        PHY是物理接口收发器,它实现物理层。包括MII/GMII(介质独立接口)子层、PCS(物理编码子层)、PMA(物理介质附加)子层、PMD(物理介质相关)子层、MDI子层。
        100BaseTX采用4B/5B编码。PHY在发送数据的时候,收到MAC过来的数据(对PHY来说,没有帧的概念,对它来说,都是数据而不管什么地址,数据还是CRC),每4bit就增加1bit的检错码,然后把并行数据转化为串行流数据,再按照物理层的编码规则把数据编码,再变为模拟信号把数据送出去。收数据时的流程反之。PHY还有个重要的功能就是实现CSMA/CD的部分功能。它可以检测到网络上是否有数据在传送,如果有数据在传送中就等待,一旦检测到网络空闲,再等待一个随机时间后将送数据出去。如果两个碰巧同时送出了数据,那样必将造成冲突,这时候,冲突检测机构可以检测到冲突,然后各等待一个随机的时间重新发送数据。这个随机时间很有讲究的,并不是一个常数,在不同的时刻计算出来的随机时间都是不同的,而且有多重算法来应付出现概率很低的同两台主机之间的第二次冲突。通信速率通过双方协商,协商的结果是两个设备中能同时支持的最大速度和最好的双工模式。这个技术被称为Auto Negotiation或者NWAY。隔离变压器把PHY送出来的差分信号用差模耦合的线圈耦合滤波以增强信号,并且通过电磁场的转换耦合到连接网线的另外一端。RJ-45中1、2是传送数据的,3、6是接收数据的。新的PHY支持AUTO MDI-X功能(也需要隔离变压器支持)。它可以实现RJ-45接口的1、2上的传送信号线和3、6上的接收信号线的功能自动互相交换

        网卡工作在osi的最后两层,物理层和数据链路层,物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。物理层的芯片称之为PHY。数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。以太网卡中数据链路层的芯片称之为MAC控制器。很多网卡的这两个部分是做到一起的。他们之间的关系是pci总线接mac总线,mac接phy,phy接网线(当然也不是直接接上的,还有一个变压装置)。
    MAC 和PHY 一个是数据链路层 一个是物理层 两者通过MII传送数据。


    4、曼彻斯特编码

        曼彻斯特编码又称曼彻斯特相位编码,它通过相位变化来实现每个位(图2)。通常,用一个时钟周期中部的上升沿表示“1”,下降沿表示“0”。周期末端的相位变化可忽略不计,但有时又可能需要将这种相位变化计算在内,这取决于前一位的值。


    5、4B/5B编码

        4B/5B编码是一种块编码方式。它将一个4位的块编码成一个5位的块。这就使5位块内永远至少包含2个“1”转换,所以在一个5位块内总能进行时钟同步。该方法需要25%的额外开销。

    二、DM9000

    1、框图

    2、芯片引脚图
    MII接口和32位总线模式可以配置
    (1)、MII接口

    (2)、32位数据总线



    展开全文
  • linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析 【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏 【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析 ...

    【linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析 

    【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏 

    【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析 

    【linux驱动分析】之dm9000驱动分析(四):net_device结构体 

    【linux驱动分析】之dm9000驱动分析(五):另外几个重要的结构体 

    【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 

    【linux驱动分析】之dm9000驱动分析(七):dm9000的卸载挂起和恢复以及打开和停止


    硬件平台:友善之臂Tiny6410核心板 + DM9000EP
    软件平台:linux-2.6.38
    交叉编译器:Friendly ARM提供的arm-linux-gcc 4.5.1
    一、源代码(mach-mini6410.c)
     1 /* Ethernet */
     2 #ifdef CONFIG_DM9000
     3 #define S3C64XX_PA_DM9000    (0x18000000)
     4 #define S3C64XX_SZ_DM9000    SZ_1M
     5 #define S3C64XX_VA_DM9000    S3C_ADDR(0x03b00300)
     6 
     7 static struct resource dm9000_resources[] = {
     8     [0] = {
     9         .start        = S3C64XX_PA_DM9000,
    10         .end        = S3C64XX_PA_DM9000 + 3,
    11         .flags        = IORESOURCE_MEM,
    12     },
    13     [1] = {
    14         .start        = S3C64XX_PA_DM9000 + 4,
    15         .end        = S3C64XX_PA_DM9000 + S3C64XX_SZ_DM9000 - 1,
    16         .flags        = IORESOURCE_MEM,
    17     },
    18     [2] = {
    19         .start        = IRQ_EINT(7),
    20         .end        = IRQ_EINT(7),
    21         .flags        = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
    22     },
    23 };
    24 
    25 static struct dm9000_plat_data dm9000_setup = {
    26     .flags            = DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY,
    27     .dev_addr        = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
    28 };
    29 
    30 static struct platform_device s3c_device_dm9000 = {
    31     .name            = "dm9000",
    32     .id                = 0,
    33     .num_resources    = ARRAY_SIZE(dm9000_resources),
    34     .resource        = dm9000_resources,
    35     .dev            = {
    36         .platform_data = &dm9000_setup,
    37     }
    38 };
    39 
    40 static int __init dm9000_set_mac(char *str) {
    41     unsigned char addr[6];
    42     unsigned int val;
    43     int idx = 0;
    44     char *p = str, *end;
    45 
    46     while (*p && idx < 6) {
    47         val = simple_strtoul(p, &end, 16);
    48         if (end <= p) {
    49             /* convert failed */
    50             break;
    51         } else {
    52             addr[idx++] = val;
    53             p = end;
    54             if (*p == ':'|| *p == '-') {
    55                 p++;
    56             } else {
    57                 break;
    58             }
    59         }
    60     }
    61 
    62     if (idx == 6) {
    63         printk("Setup ethernet address to %pM\n", addr);
    64         memcpy(dm9000_setup.param_addr, addr, 6);
    65     }
    66 
    67     return 1;
    68 }
    69 
    70 __setup("ethmac=", dm9000_set_mac);
    71 #endif
    72 
    73 static struct map_desc mini6410_iodesc[] = {
    74     {
    75         /* LCD support */
    76         .virtual    = (unsigned long)S3C_VA_LCD,
    77         .pfn        = __phys_to_pfn(S3C_PA_FB),
    78         .length     = SZ_16K,
    79         .type       = MT_DEVICE,
    80     },
    81 #ifdef CONFIG_DM9000           /*这里的定义不知道是做什么用的*/
    82     {
    83         .virtual    = (u32)S3C64XX_VA_DM9000,
    84         .pfn        = __phys_to_pfn(S3C64XX_PA_DM9000),
    85         .length        = S3C64XX_SZ_DM9000,
    86         .type        = MT_DEVICE,
    87     },
    88 #endif
    89 };
    DM9000的设备会在
    platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices));
    里统一注册。

    二、下面分析一下上面代码中红色的宏或者函数
    1、ARRAY_SIZE
      #define  ARRAY_SIZE(arr)   (sizeof(arr) / sizeof( (arr)[0] )  +  __must_be_array(arr) )
    它是定义在include/linux/kernel.h中的一个宏,用来计算数组中元素的个数。
    __must_be_array是编译器相关的,用来防止传入的参数不是数组,比如说传入了指针,这样的话可能回编译不通过(猜测)。
    2、simple_strtoul
    /**
     * simple_strtoul - convert a string to an unsigned long
     * @cp: The start of the string
     * @endp: A pointer to the end of the parsed string will be placed here
     * @base: The number base to use
     */
    unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
    simple_strtoul是定义在lib/vsprintf.c中的函数,它的作用是把一个字符串转换为unsigned long型的整数,并返回。
    其中的endp参数存放解析后的字符串地址,base参数,是要转换的进制数。
    vsprintf.c里还定义了其他好多字符串处理的函数,具体用到时去查。

    3、__setup
    它是定义在include/linux/init.h中的一个宏:
    #define __setup(str, fn)                    \
        __setup_param(str, fn, fn, 0)

    其中:str是关键字,fn是关联处理函数。__setup只是告诉内核在启动时输入串中含有str时,内核要去执行fn。Str必须以“=”符结束以使parse_args更方便解析。紧随“=”后的任何文本都会作为输入传给fn。

    例如本例中的:__setup("ethmac=", dm9000_set_mac);
    关于__setup的更多分析见《__setup宏的作用》

    展开全文
  • /*分析DM9000网卡驱动之初始化*/ /*找到DM9000.c 文件路径: linux/drivers/net下 找到模块的入口函数处 */ static int __init dm9000_init(void) { printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, ...
  • 之前已经把uboot,内核,文件系统,都移植好了,今天开始我们把第二期写的Linux2.6.22.6内核的驱动程序全部移植到我们新3.4.2内核中去。首先移植网卡驱动程序吧,因为,我们后面需要用到网络文件系统来更简便的学习...
  • 根据原厂网卡驱动移植DM9000C
  • 基于linux2.6.30.4内核的DM9000网卡驱动编译成模块成功ping通
  • 文档时间:2018-08-25 ...1,移植内核自带的 DM9000 网卡驱动 使用之前制作的 uboot,kernel 和 文件系统,在 uboot 终端把 machid 设置为 0x16a (SMDK2440),启动内核,然后输入 ifconfig 命令,发现不...
  • 1.启动开发板,此时uboot环境变量没有设置machid,那么machid一定会采用在uboot代码中的初始化: 果然,此时的machid = id of smdk2410 (当然,此时的... 显然smdk2410检测不到网卡驱动。 2.2smdk2440 ...
  • linux Dm9000 驱动分析

    2012-09-11 10:49:13
    虽然Linux驱动程序应该是和具体的硬件平台分离的,但是为了更好的理解DM9000驱动程序,这里还是结合一下Mini2440开发板,这样也可以更好的体会如何实现驱动和平台分离。 本文分成以下几个部分: 一、Mini2440...
  • ******************************************************************************************************************* #主机操作系统:CentOS 6.7 #交叉编译环境:arm-linux-gcc ...#linux内核版本:linux-3.0
  • 本文所分析的DM9000驱动,是基于platform设备模型的。 网络驱动程序不再是对文件进行操作,而是由专门的网络接口struct net_device来实现。应用程序不能直接访问网络驱动程序,只能由网络字系统与它交互。此外,不...
  • <br />Linux2.6 移植:DM9000 驱动 工作环境:Ubuntu10.0.4 交叉编译环境:3.4.1 板子:MagicARM2410 系统内核:linux2.6.24.4 移植DM9000 驱动前需要知道的DM9000 的硬件...
  • mini2440 DM9000驱动分析

    2015-07-04 13:44:49
    最好结合DM9000、S3C2440手册分析; DM9000手册: http://wenku.baidu.com/link?url=5I7F6EgEJlb5p2O3DbibOepm252TNj6ZXAF0v0G3BpHy9i6bo19Fr6oo-1boH-Ml1_MQGQGUvMXAz7kmCeyAtc7rNl_00bU8z9ymhffLWl_  S3C2440...
1 2 3 4 5 ... 20
收藏数 3,222
精华内容 1,288
关键字:

dm9000驱动 linux