精华内容
下载资源
问答
  • DM9000驱动程序详解

    千次阅读 2013-03-21 13:59:34
    //arch/arm/mach-s5pv210/mach-smdkv210.c /*片选信号使用的是XM0CSn1,查看S5PV210的芯片手册内存映射一节可以找到SROM BANK1的地址范围是0x8800_0000 --0x8FFF_FFFF, ...#define S5PV210_PA_DM9000_A (0x880010
    //arch/arm/mach-s5pv210/mach-smdkv210.c
    
    /*片选信号使用的是XM0CSn1,查看S5PV210的芯片手册内存映射一节可以找到SROM BANK1的地址范围是0x8800_0000 --0x8FFF_FFFF,
       则访问这个范围的地址时会激活片选使能信号XM0CSn1*/
    #define S5PV210_PA_DM9000_A     (0x88001000) 
    #define S5PV210_PA_DM9000_F     (S5PV210_PA_DM9000_A + 0x300C)
    //
    static struct resource smdkv210_dm9000_resources[] = {
        [0] = {
            .start  = S5PV210_PA_DM9000_A, //地址线
            .end    = S5PV210_PA_DM9000_A + SZ_1K*4 - 1,
            .flags  = IORESOURCE_MEM,
        },
        [1] = {
            .start  = S5PV210_PA_DM9000_F, //数据线
            .end    = S5PV210_PA_DM9000_F + SZ_1K*4 - 1,
            .flags  = IORESOURCE_MEM,
        },  
        [2] = { 
            .start  = IRQ_EINT(7),
            .end    = IRQ_EINT(7),
            .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,//中断资源和高频存储发资源
        },
    };
    


    //arch/arm/mach-s5pv210/mach-smdkv210.c
    
    static struct dm9000_plat_data smdkv210_dm9000_platdata = { .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM, //16bit模式 .dev_addr = { 0x08, 0x90, 0x00, 0xa0, 0x02, 0x10 }, //设置网卡的物理地址 };
    
    
    struct platform_device smdkv210_dm9000 = {
        .name       = "dm9000",  //平台设备名
        .id     = -1,
        .num_resources  = ARRAY_SIZE(smdkv210_dm9000_resources), //资源数组个数
        .resource   = smdkv210_dm9000_resources, //资源数组
        .dev        = {
            .platform_data  = &smdkv210_dm9000_platdata, //平台设备数据
        },
    };
    
    static struct platform_device *smdkv210_devices[] __initdata = {
         ...
    &smdkv210_dm9000,
    ...
    };
    
    static void __init smdkv210_dm9000_init(void)
    {
        unsigned int tmp;
    
        gpio_request(S5PV210_MP01(1), "nCS1");
        s3c_gpio_cfgpin(S5PV210_MP01(1), S3C_GPIO_SFN(2));
        gpio_free(S5PV210_MP01(1));
    
        tmp = (5 << S5P_SROM_BCX__TACC__SHIFT);
        __raw_writel(tmp, S5P_SROM_BC1);
    
        tmp = __raw_readl(S5P_SROM_BW);
        tmp &= (S5P_SROM_BW__CS_MASK << S5P_SROM_BW__NCS1__SHIFT);
        tmp |= (1 << S5P_SROM_BW__NCS1__SHIFT);
        __raw_writel(tmp, S5P_SROM_BW);
    }
    
    
    static void __init smdkv210_machine_init(void)
    {
    s3c_pm_init();
    
        smdkv210_dm9000_init(); //dm9000初始化
    ....
    
        platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));//将所有设备添加到系统
    
    };
    
    
    MACHINE_START(SMDKV210, "SMDKV210")
        /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
        .boot_params    = S5P_PA_SDRAM + 0x100,
        .init_irq   = s5pv210_init_irq,
        .map_io     = smdkv210_map_io,
        .init_machine   = smdkv210_machine_init, //机器初始化
        .timer      = &s5p_timer,
    MACHINE_END
    

    //drivers/base/platform.c
    int platform_add_devices(struct platform_device **devs, int num)
    {   
        int i, ret = 0;
    
        for (i = 0; i < num; i++) {
            ret = platform_device_register(devs[i]);
            if (ret) {  
                while (--i >= 0)
                    platform_device_unregister(devs[i]);
                break;  
            }
        }
    
        return ret;
    }


    展开全文
  • mini2440 dm9000 网卡驱动详解 (一)

    千次阅读 2013-07-31 19:16:19
    mini2440 dm9000 网卡驱动详解 (一) 虽然Linux驱动程序应该是和具体的硬件平台分离的,但是为了更好的理解DM9000的驱动程序,这里还是结合一下Mini2440开发板,这样也可以更好的体会如何实现驱动和平台分离。  ...
    mini2440 dm9000 网卡驱动详解 (一)

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

     

    本文分成以下几个部分:

    一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系。 

     

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

    三、具体代码分析

     

    一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系

    Mini2440开发板上DM9000与S3C2440的连接关系如下:


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

    其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x20000000开始。DM9000的TXD[2:0]作为strap pin在电路图中是空接的,所以IO base是300H。中断使用了EINT7。这些内容在Mach文件中有如下体现:


    1. #define S3C2410_CS4 (0x20000000)     
    2. #define MACH_MINI2440_DM9K_BASE (S3C2410_CS4 + 0x300)      
    3. static struct resource mini2440_dm9k_resource[] __initdata = {       
    4.     [0] = {       
    5.         .start = MACH_MINI2440_DM9K_BASE,       
    6.         .end   = MACH_MINI2440_DM9K_BASE + 3,       
    7.         .flags = IORESOURCE_MEM       
    8.     },       
    9.     [1] = {       
    10.         .start = MACH_MINI2440_DM9K_BASE + 4,       
    11.         .end   = MACH_MINI2440_DM9K_BASE + 7,       
    12.         .flags = IORESOURCE_MEM       
    13.     },       
    14.     [2] = {       
    15.         .start = IRQ_EINT7,       
    16.         .end   = IRQ_EINT7,       
    17.         .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,       
    18.     }       
    19. };    

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

    1. static struct dm9000_plat_data mini2440_dm9k_pdata __initdata = {      
    2.     .flags      = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),      
    3. };      
    4.      
    5. static struct platform_device mini2440_device_eth __initdata = {      
    6.     .name       = "dm9000",      
    7.     .id     = -1,      
    8.     .num_resources  = ARRAY_SIZE(mini2440_dm9k_resource),      
    9.     .resource   = mini2440_dm9k_resource,      
    10.     .dev        = {      
    11.         .platform_data  = &mini2440_dm9k_pdata,      
    12.     },      
    13. };    
    14.   
    15.    
    16. MACHINE_START(MINI2440, "MINI2440")      
    17.     /* Maintainer: Michel Pollet <buserror@gmail.com> */     
    18.     .phys_io    = S3C2410_PA_UART,      
    19.     .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,      
    20.     .boot_params    = S3C2410_SDRAM_PA + 0x100,      
    21.     .map_io     = mini2440_map_io,      
    22.     .init_machine   = mini2440_init, /*初始化函数*/     
    23.     .init_irq   = s3c24xx_init_irq,      
    24.     .timer      = &s3c24xx_timer,      
    25. MACHINE_END    
    26.      
    27.   
    28.   
    29.    
    30. static void __init mini2440_init(void)      
    31. {      
    32.     ...      
    33.          ...      
    34.     platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));      
    35.      
    36.         ...      
    37.         ...      
    38. }    
    39.   
    40.   
    41.    
    42. static struct platform_device *mini2440_devices[] __initdata = {      
    43.     &s3c_device_usb,      
    44.     &s3c_device_wdt,      
    45. /*  &s3c_device_adc,*/ /* ADC doesn't like living with touchscreen ! */     
    46.     &s3c_device_i2c0,      
    47.     &s3c_device_rtc,      
    48.     &s3c_device_usbgadget,      
    49.     &mini2440_device_eth, /*dm9000是众多平台设备中的一个*/     
    50.     &mini2440_led1,      
    51.     &mini2440_led2,      
    52.     &mini2440_led3,      
    53.     &mini2440_led4,      
    54.     &mini2440_button_device,      
    55.     &s3c_device_nand,      
    56.     &s3c_device_sdi,      
    57.     &s3c_device_iis,      
    58.     &mini2440_audio,      
    59. /*  &s3c_device_timer[0],*/ /* buzzer pwm, no API for it */     
    60.     /* remaining devices are optional */     
    61. };    

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

     *sk_buff

     

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

     

     *net_device

     

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

     

    三、具体代码的分析

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

     

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

    1. static struct platform_driver dm9000_driver = {      
    2.     .driver = {      
    3.         .name    = "dm9000",      
    4.         .owner   = THIS_MODULE,      
    5.     },      
    6.     .probe   = dm9000_probe,      
    7.     .remove  = __devexit_p(dm9000_drv_remove),      
    8.     .suspend = dm9000_drv_suspend,      
    9.     .resume  = dm9000_drv_resume,      
    10. };    

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

     

    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. };    


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


    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_eeprom_len     = dm9000_get_eeprom_len,      
    10.     .get_eeprom     = dm9000_get_eeprom,      
    11.     .set_eeprom     = dm9000_set_eeprom,      
    12. };    

    *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     dbug_cnt;      
    12.     u8      io_mode;        /* 0:word, 2:byte */     
    13.     u8      phy_addr;      
    14.     u8      imr_all;      
    15.      
    16.     unsigned int    flags;      
    17.     unsigned int    in_suspend :1;      
    18.     int     debug_level;      
    19.      
    20.     enum dm9000_type type;      
    21.      
    22.     void (*inblk)(void __iomem *port, void *data, int length);      
    23.     void (*outblk)(void __iomem *port, void *data, int length);      
    24.     void (*dumpblk)(void __iomem *port, int length);      
    25.      
    26.     struct device   *dev;        /* parent device */     
    27.      
    28.     struct resource *addr_res;   /* resources found */     
    29.     struct resource *data_res;      
    30.     struct resource *addr_req;   /* resources requested */     
    31.     struct resource *data_req;      
    32.     struct resource *irq_res;      
    33.      
    34.     struct mutex     addr_lock; /* phy and eeprom access lock */     
    35.      
    36.     struct delayed_work phy_poll;      
    37.     struct net_device  *ndev;      
    38.      
    39.     spinlock_t  lock;      
    40.      
    41.     struct mii_if_info mii;      
    42.     u32     msg_enable;      
    43. } board_info_t;  

    下面看一下具体代码。

     

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

     

        1. 注册平台驱动。

     

     

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

    1. static struct platform_driver dm9000_driver = {      
    2.     .driver = {      
    3.         .name    = "dm9000"/*用这个名字完成驱动和设备的match*/     
    4.         .owner   = THIS_MODULE,      
    5.     },      
    6.     .probe   = dm9000_probe,      
    7.     .remove  = __devexit_p(dm9000_drv_remove),      
    8.     .suspend = dm9000_drv_suspend,      
    9.     .resume  = dm9000_drv_resume,      
    10. };      
    11.      
    12. static int __init      
    13. dm9000_init(void)      
    14. {      
    15.     printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);      
    16.      
    17.     return platform_driver_register(&dm9000_driver);      
    18. }    

     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。

    下面是代码清单:

    1.    
    2. static int __devinit      
    3. dm9000_probe(struct platform_device *pdev)      
    4. {      
    5.     struct dm9000_plat_data *pdata = pdev->dev.platform_data;      
    6.     struct board_info *db;  /* Point a board information structure */     
    7.     struct net_device *ndev;      
    8.     const unsigned char *mac_src;      
    9.     int ret = 0;      
    10.     int iosize;      
    11.     int i;      
    12.     u32 id_val;      
    13.      
    14.     /* Init network device */     
    15.         /*使用alloc_etherdev()来生成一个net_device结构体,并对其公有成员赋值*/     
    16.     ndev = alloc_etherdev(sizeof(struct board_info));      
    17.     if (!ndev) {      
    18.         dev_err(&pdev->dev, "could not allocate device.\n");      
    19.         return -ENOMEM;      
    20.     }      
    21.      
    22.     SET_NETDEV_DEV(ndev, &pdev->dev);      
    23.      
    24.     dev_dbg(&pdev->dev, "dm9000_probe()\n");      
    25.      
    26.     /* setup board info structure */     
    27.     db = netdev_priv(ndev);      
    28.     memset(db, 0, sizeof(*db));      
    29.      
    30.     db->dev = &pdev->dev;      
    31.     db->ndev = ndev;      
    32.      
    33.     spin_lock_init(&db->lock);      
    34.     mutex_init(&db->addr_lock);      
    35.      
    36.     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);      
    37.      
    38.     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);      
    39.     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);      
    40.     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);      
    41.      
    42.     if (db->addr_res == NULL || db->data_res == NULL ||      
    43.         db->irq_res == NULL) {      
    44.         dev_err(db->dev, "insufficient resources\n");      
    45.         ret = -ENOENT;      
    46.         goto out;      
    47.     }      
    48.      
    49.     iosize = res_size(db->addr_res);      
    50.     db->addr_req = request_mem_region(db->addr_res->start, iosize,      
    51.                       pdev->name);      
    52.      
    53.     if (db->addr_req == NULL) {      
    54.         dev_err(db->dev, "cannot claim address reg area\n");      
    55.         ret = -EIO;      
    56.         goto out;      
    57.     }      
    58.      
    59.     db->io_addr = ioremap(db->addr_res->start, iosize);      
    60.      
    61.     if (db->io_addr == NULL) {      
    62.         dev_err(db->dev, "failed to ioremap address reg\n");      
    63.         ret = -EINVAL;      
    64.         goto out;      
    65.     }      
    66.      
    67.     iosize = res_size(db->data_res);      
    68.     db->data_req = request_mem_region(db->data_res->start, iosize,      
    69.                       pdev->name);      
    70.      
    71.     if (db->data_req == NULL) {      
    72.         dev_err(db->dev, "cannot claim data reg area\n");      
    73.         ret = -EIO;      
    74.         goto out;      
    75.     }      
    76.      
    77.     db->io_data = ioremap(db->data_res->start, iosize);      
    78.      
    79.     if (db->io_data == NULL) {      
    80.         dev_err(db->dev, "failed to ioremap data reg\n");      
    81.         ret = -EINVAL;      
    82.         goto out;      
    83.     }      
    84.      
    85.     /* fill in parameters for net-dev structure */     
    86.     ndev->base_addr = (unsigned long)db->io_addr;      
    87.     ndev->irq    = db->irq_res->start;      
    88.      
    89.     /* ensure at least we have a default set of IO routines */     
    90.     dm9000_set_io(db, iosize);      
    91.      
    92.     /* check to see if anything is being over-ridden */     
    93.     if (pdata != NULL) {      
    94.         /* check to see if the driver wants to over-ride the    
    95.          * default IO width */     
    96.      
    97.         if (pdata->flags & DM9000_PLATF_8BITONLY)      
    98.             dm9000_set_io(db, 1);      
    99.      
    100.         if (pdata->flags & DM9000_PLATF_16BITONLY)      
    101.             dm9000_set_io(db, 2);      
    102.      
    103.         if (pdata->flags & DM9000_PLATF_32BITONLY)      
    104.             dm9000_set_io(db, 4);      
    105.      
    106.         /* check to see if there are any IO routine    
    107.          * over-rides */     
    108.      
    109.         if (pdata->inblk != NULL)      
    110.             db->inblk = pdata->inblk;      
    111.      
    112.         if (pdata->outblk != NULL)      
    113.             db->outblk = pdata->outblk;      
    114.      
    115.         if (pdata->dumpblk != NULL)      
    116.             db->dumpblk = pdata->dumpblk;      
    117.      
    118.         db->flags = pdata->flags;      
    119.     }     
    120.     
    121. #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL      
    122.     db->flags |= DM9000_PLATF_SIMPLE_PHY;     
    123. #endif      
    124.      
    125.     dm9000_reset(db);      
    126.      
    127.     /* try multiple times, DM9000 sometimes gets the read wrong */     
    128.     for (i = 0; i < 8; i++) {      
    129.         id_val  = ior(db, DM9000_VIDL);      
    130.         id_val |= (u32)ior(db, DM9000_VIDH) << 8;      
    131.         id_val |= (u32)ior(db, DM9000_PIDL) << 16;      
    132.         id_val |= (u32)ior(db, DM9000_PIDH) << 24;      
    133.      
    134.         if (id_val == DM9000_ID)      
    135.             break;      
    136.         dev_err(db->dev, "read wrong id 0x%08x\n", id_val);      
    137.     }      
    138.      
    139.     if (id_val != DM9000_ID) {      
    140.         dev_err(db->dev, "wrong id: 0x%08x\n", id_val);      
    141.         ret = -ENODEV;      
    142.         goto out;      
    143.     }      
    144.      
    145.     /* Identify what type of DM9000 we are working on */     
    146.      
    147.     id_val = ior(db, DM9000_CHIPR);      
    148.     dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);      
    149.      
    150.     switch (id_val) {      
    151.     case CHIPR_DM9000A:      
    152.         db->type = TYPE_DM9000A;      
    153.         break;      
    154.     case CHIPR_DM9000B:      
    155.         db->type = TYPE_DM9000B;      
    156.         break;      
    157.     default:      
    158.         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);      
    159.         db->type = TYPE_DM9000E;      
    160.     }      
    161.      
    162.     /* from this point we assume that we have found a DM9000 */     
    163.      
    164.     /* driver system function */     
    165.     ether_setup(ndev);      
    166.      
    167.     ndev->netdev_ops = &dm9000_netdev_ops;      
    168.     ndev->watchdog_timeo = msecs_to_jiffies(watchdog);      
    169.     ndev->ethtool_ops    = &dm9000_ethtool_ops;      
    170.      
    171.     db->msg_enable       = NETIF_MSG_LINK;      
    172.     db->mii.phy_id_mask  = 0x1f;      
    173.     db->mii.reg_num_mask = 0x1f;      
    174.     db->mii.force_media  = 0;      
    175.     db->mii.full_duplex  = 0;      
    176.     db->mii.dev       = ndev;      
    177.     db->mii.mdio_read    = dm9000_phy_read;      
    178.     db->mii.mdio_write   = dm9000_phy_write;      
    179.      
    180.     mac_src = "eeprom";      
    181.      
    182.     /* try reading the node address from the attached EEPROM */     
    183.     for (i = 0; i < 6; i += 2)      
    184.         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);      
    185.      
    186.     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {      
    187.         mac_src = "platform data";      
    188.         memcpy(ndev->dev_addr, pdata->dev_addr, 6);      
    189.     }      
    190.      
    191.     if (!is_valid_ether_addr(ndev->dev_addr)) {      
    192.         /* try reading from mac */     
    193.               
    194.         mac_src = "chip";      
    195.         for (i = 0; i < 6; i++)      
    196.             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);      
    197.     }      
    198.      
    199.     if (!is_valid_ether_addr(ndev->dev_addr))      
    200.         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "     
    201.              "set using ifconfig\n", ndev->name);      
    202.      
    203.     platform_set_drvdata(pdev, ndev);      
    204.     ret = register_netdev(ndev);      
    205.      
    206.     if (ret == 0)      
    207.         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",      
    208.                ndev->name, dm9000_type_to_char(db->type),      
    209.                db->io_addr, db->io_data, ndev->irq,      
    210.                ndev->dev_addr, mac_src);      
    211.     return 0;      
    212.      
    213. out:      
    214.     dev_err(db->dev, "not found (%d).\n", ret);      
    215.      
    216.     dm9000_release_board(pdev, db);      
    217.     free_netdev(ndev);      
    218.      
    219.     return ret;      
    220. }  
    展开全文
  • mini2440 dm9000 网卡驱动详解 (三)

    千次阅读 2013-07-31 19:28:00
    mini2440 dm9000 网卡驱动详解 (三) *dm9000_get_drvinfo()    该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool_drvinfo结构体变量。代码清单如下:     [cpp] view ...
    mini2440 dm9000 网卡驱动详解 (三)

    *dm9000_get_drvinfo()

     

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

     

     

    1. static void dm9000_get_drvinfo(struct net_device *dev,      
    2.                    struct ethtool_drvinfo *info)      
    3. {      
    4.     board_info_t *dm = to_dm9000_board(dev); /*to_dm9000_board实际上就是调用了netdev_priv(dev)*/     
    5.      
    6.     strcpy(info->driver, CARDNAME);      
    7.     strcpy(info->version, DRV_VERSION);      
    8.     strcpy(info->bus_info, to_platform_device(dm->dev)->name);      
    9. }    

       *dm9000_get_settings()

     

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

     

       *dm9000_set_settings()

     

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

     

       *dm9000_get_msglevel()

     

       *dm9000_set_msglevel()

     

        这两个函数设置和取得messagelevel,实际是设置和取得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:

    1. u8 RX_ready = ior( IOaddr, 0xF0 );         /* dummy read the packet ready flag */     
    2. RX_ready = (u8) inp( IOaddr + 4 );         /* got the most updated data */     
    3. if ( RX_ready == 1 ) {                     /* ready check: this byte must be 0 or 1 */     
    4.        /* check the RX status and to get RX length (see datasheet ch.5.6.3) */     
    5.        /* income RX a packet (see datasheet ch.5.6.4) */     
    6. else if ( RX_ready != 0 ) {              /* stop device and wait to reset device */     
    7.        iow( IOaddr, 0xFF, 0x80 );          /* stop INT request */     
    8.        iow( IOaddr, 0xFE, 0x0F );          /* clear ISR status */     
    9.        iow( IOaddr, 0x05, 0x00 );          /* stop RX function */     
    10.        u8 device_wait_reset = TRUE;        /* raise the reset flag */     
    11. }    

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

     

    1. u8 io_mode = ior( IOaddr, 0xFE ) >> 6; /* ISR bit[7:6] keep I/O mode */     
    2. outp( IOaddr, 0xF2 );                /* trigger MRCMD reg_F2h with read_ptr++ */     
    3. /* int RX_status : the RX packet status, int RX_length : the RX packet length */     
    4. if ( io_mode == 2 ) {                /* I/O byte mode */     
    5. RX_status = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 );      
    6. RX_length = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 );        }      
    7. else if ( io_mode == 0 ) {           /* I/O word mode */     
    8. RX_status = inpw( IOaddr + 4 );      
    9. RX_length = inpw( IOaddr + 4 );             }      
    10. else if ( io_mode == 1 ) {           /* I/O dword mode */     
    11. (u32) status_tmp = inpl( IOaddr + 4 );           /* got the RX 32-bit dword data */     
    12. RX_status = (u16)( status_tmp & 0xFFFF );      
    13. RX_length = (u16)( ( status_tmp >> 16 ) & 0xFFFF );          }    

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

    1. /* u8 RX_data[] : the data of the received packet */     
    2. if ( io_mode == 2 ) {                 /* I/O byte mode */     
    3. for ( i = 0 ; i < RX_length ; i++ ) /* loop to read a byte data from RX SRAM */     
    4.   RX_data[ i ] = (u8) inp( IOaddr + 4 );          }      
    5. else if ( io_mode == 0 ) {            /* I/O word mode */     
    6. int length_tmp = ( RX_length + 1 ) / 2;      
    7. for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a word data from RX SRAM */     
    8.  ( (u16 *)RX_data)[ i ] = inpw( IOaddr + 4 );           }      
    9. else if ( io_mode == 1 ) {            /* I/O dword mode */     
    10. int length_tmp = ( RX_length + 3 ) / 4;      
    11. for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a dword data from RX SRAM */     
    12.  ( (u32 *)RX_data)[ i ] = inpl( IOaddr + 4 ); }         /* inpl() is inport 32-bit I/O */    

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

     

    1. struct dm9000_rxhdr {      
    2.     u8  RxPktReady;      
    3.     u8  RxStatus;      
    4.     __le16  RxLen;      
    5. } __attribute__((__packed__));    

    接收函数代码如下:

     

    1. /*    
    2.  *  Received a packet and pass to upper layer    
    3.  */     
    4. static void     
    5. dm9000_rx(struct net_device *dev)      
    6. {      
    7.     board_info_t *db = netdev_priv(dev);      
    8.     struct dm9000_rxhdr rxhdr;      
    9.     struct sk_buff *skb;      
    10.     u8 rxbyte, *rdptr;      
    11.     bool GoodPacket;      
    12.     int RxLen;      
    13.      
    14.     /* Check packet ready or not */     
    15.     do {      
    16.         ior(db, DM9000_MRCMDX); /* Dummy read */     
    17.      
    18.         /* Get most updated data */     
    19.                 /*读一下最新数据的第一个字节*/     
    20.         rxbyte = readb(db->io_data);      
    21.      
    22.         /* Status check: this byte must be 0 or 1 */     
    23.                 /*DM9000_PKT_RDY定义是0x01,如果第一个字节大于0x01,则不是正确的状态。因为第一个字节只能是01h或00h*/     
    24.         if (rxbyte > DM9000_PKT_RDY) {      
    25.             dev_warn(db->dev, "status check fail: %d\n", rxbyte);      
    26.             iow(db, DM9000_RCR, 0x00);  /* Stop Device */     
    27.             iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */     
    28.             return;      
    29.         }      
    30.      
    31.         if (rxbyte != DM9000_PKT_RDY)      
    32.             return;      
    33.      
    34.         /* A packet ready now  & Get status/length */     
    35.         GoodPacket = true;      
    36.         writeb(DM9000_MRCMD, db->io_addr);      
    37.      
    38.         (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));/*一次性读入四个字节的内容到rxhdr变量*/     
    39.      
    40.         RxLen = le16_to_cpu(rxhdr.RxLen);      
    41.      
    42.         if (netif_msg_rx_status(db))      
    43.             dev_dbg(db->dev, "RX: status %02x, length %04x\n",      
    44.                 rxhdr.RxStatus, RxLen);      
    45.      
    46.         /* Packet Status check */     
    47.         if (RxLen < 0x40) {      
    48.             GoodPacket = false;      
    49.             if (netif_msg_rx_err(db))      
    50.                 dev_dbg(db->dev, "RX: Bad Packet (runt)\n");      
    51.         }      
    52.      
    53.         if (RxLen > DM9000_PKT_MAX) {      
    54.             dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);      
    55.         }      
    56.      
    57.         /* rxhdr.RxStatus is identical to RSR register. */     
    58.         if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |      
    59.                       RSR_PLE | RSR_RWTO |      
    60.                       RSR_LCS | RSR_RF)) {      
    61.             GoodPacket = false;      
    62.             if (rxhdr.RxStatus & RSR_FOE) {      
    63.                 if (netif_msg_rx_err(db))      
    64.                     dev_dbg(db->dev, "fifo error\n");      
    65.                 dev->stats.rx_fifo_errors++;      
    66.             }      
    67.             if (rxhdr.RxStatus & RSR_CE) {      
    68.                 if (netif_msg_rx_err(db))      
    69.                     dev_dbg(db->dev, "crc error\n");      
    70.                 dev->stats.rx_crc_errors++;      
    71.             }      
    72.             if (rxhdr.RxStatus & RSR_RF) {      
    73.                 if (netif_msg_rx_err(db))      
    74.                     dev_dbg(db->dev, "length error\n");      
    75.                 dev->stats.rx_length_errors++;      
    76.             }      
    77.         }      
    78.      
    79.         /* Move data from DM9000 */     
    80.                 /*关键的代码就是这里啦。使用到了上面提到的sk_buff。将RX SRAM中的data段数据放入sk_buff,然后发送给上层,至于怎么发送,不用去驱动操心了。sk_buff的protocol全部搞定*/     
    81.         if (GoodPacket      
    82.             && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {      
    83.             skb_reserve(skb, 2);      
    84.             rdptr = (u8 *) skb_put(skb, RxLen - 4);      
    85.      
    86.             /* Read received packet from RX SRAM */     
    87.      
    88.             (db->inblk)(db->io_data, rdptr, RxLen);      
    89.             dev->stats.rx_bytes += RxLen;      
    90.      
    91.             /* Pass to upper layer */     
    92.             skb->protocol = eth_type_trans(skb, dev);      
    93.             netif_rx(skb);      
    94.             dev->stats.rx_packets++;      
    95.      
    96.         } else {      
    97.             /* need to dump the packet's data */     
    98.      
    99.             (db->dumpblk)(db->io_data, RxLen);      
    100.         }      
    101.     } while (rxbyte == DM9000_PKT_RDY);      
    102. }    

    6. 中断处理相关函数

     

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

     

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

     

    1. static irqreturn_t dm9000_interrupt(int irq, void *dev_id)      
    2. {      
    3.     struct net_device *dev = dev_id;      
    4.     board_info_t *db = netdev_priv(dev);      
    5.     int int_status;      
    6.     unsigned long flags;      
    7.     u8 reg_save;      
    8.      
    9.     dm9000_dbg(db, 3, "entering %s\n", __func__);      
    10.      
    11.     /* A real interrupt coming */     
    12.      
    13.     /* holders of db->lock must always block IRQs */     
    14.     spin_lock_irqsave(&db->lock, flags);      
    15.      
    16.     /* Save previous register address */     
    17.     reg_save = readb(db->io_addr);      
    18.      
    19.     /* Disable all interrupts */     
    20.     iow(db, DM9000_IMR, IMR_PAR);      
    21.      
    22.     /* Got DM9000 interrupt status */     
    23.     int_status = ior(db, DM9000_ISR);   /* Got ISR */     
    24.     iow(db, DM9000_ISR, int_status);    /* Clear ISR status */     
    25.      
    26.     if (netif_msg_intr(db))      
    27.         dev_dbg(db->dev, "interrupt status %02x\n", int_status);      
    28.      
    29.     /* Received the coming packet */     
    30.         /*如果是由于收到数据而触发的中断,显然调用dm9000_rx()把数据取走,传递给上层*/     
    31.     if (int_status & ISR_PRS)      
    32.         dm9000_rx(dev);      
    33.      
    34.     /* Trnasmit Interrupt check */     
    35.         /*如果是由于发送完了数据而触发的中断,则调用dm9000_tx_done()函数,下面具体分析这个函数*/     
    36.     if (int_status & ISR_PTS)      
    37.         dm9000_tx_done(dev, db);      
    38.      
    39.     if (db->type != TYPE_DM9000E) {      
    40.         if (int_status & ISR_LNKCHNG) {      
    41.             /* fire a link-change request */     
    42.             schedule_delayed_work(&db->phy_poll, 1);      
    43.         }      
    44.     }      
    45.      
    46.     /* Re-enable interrupt mask */     
    47.     iow(db, DM9000_IMR, db->imr_all);      
    48.      
    49.     /* Restore previous register address */     
    50.     writeb(reg_save, db->io_addr);      
    51.      
    52.     spin_unlock_irqrestore(&db->lock, flags);      
    53.      
    54.     return IRQ_HANDLED;      
    55. }    

    *dm9000_tx_done()

     

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

     

     

     

        (1)读取dm9000寄存器NSR(NetworkStatus 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)通知内核可以将待发送的数据包进入发送队列。

     

    1. /*    
    2.  * DM9000 interrupt handler    
    3.  * receive the packet to upper layer, free the transmitted packet    
    4.  */     
    5.      
    6. static void dm9000_tx_done(struct net_device *dev, board_info_t *db)      
    7. {      
    8.     int tx_status = ior(db, DM9000_NSR);    /* Got TX status */     
    9.      
    10.     if (tx_status & (NSR_TX2END | NSR_TX1END)) {      
    11.         /* One packet sent complete */     
    12.         db->tx_pkt_cnt--;      
    13.         dev->stats.tx_packets++;      
    14.      
    15.         if (netif_msg_tx_done(db))      
    16.             dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);      
    17.      
    18.         /* Queue packet check & send */     
    19.         if (db->tx_pkt_cnt > 0) {      
    20.             iow(db, DM9000_TXPLL, db->queue_pkt_len);      
    21.             iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8);      
    22.             iow(db, DM9000_TCR, TCR_TXREQ);      
    23.             dev->trans_start = jiffies;      
    24.         }      
    25.         netif_wake_queue(dev);      
    26.     }      
    27. }    

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

     

       在看函数之前还是先来看一下DM9000CMD 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端口读一个字节。代码清单如下:

    1. static u8 ior(board_info_t * db, int reg)      
    2. {      
    3.     writeb(reg, db->io_addr); /*写reg到ADDR_PORT,用来选择寄存器*/     
    4.     return readb(db->io_data); /*从DATA_PORT读一个字节,用来读寄存器*/     
    5. }    
    6.   
    7.     * iow()  
    8.   
    9.         向IO端口写一个字节。代码清单如下:  
    10.   
    11. /*    
    12.  *   Write a byte to I/O port    
    13.  */     
    14.      
    15. static void     
    16. iow(board_info_t * db, int reg, int value)      
    17. {      
    18.     writeb(reg, db->io_addr);      
    19.     writeb(value, db->io_data);      
    20. }    

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

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

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

     

    本文分成以下几个部分:

    一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系。 

     

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

    三、具体代码分析

     

    一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系

    Mini2440开发板上DM9000与S3C2440的连接关系如下:


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

    其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x20000000开始。DM9000的TXD[2:0]作为strap pin在电路图中是空接的,所以IO base是300H。中断使用了EINT7。这些内容在Mach文件中有如下体现:


    1. #define S3C2410_CS4 (0x20000000)     
    2. #define MACH_MINI2440_DM9K_BASE (S3C2410_CS4 + 0x300)      
    3. static struct resource mini2440_dm9k_resource[] __initdata = {       
    4.     [0] = {       
    5.         .start = MACH_MINI2440_DM9K_BASE,       
    6.         .end   = MACH_MINI2440_DM9K_BASE + 3,       
    7.         .flags = IORESOURCE_MEM       
    8.     },       
    9.     [1] = {       
    10.         .start = MACH_MINI2440_DM9K_BASE + 4,       
    11.         .end   = MACH_MINI2440_DM9K_BASE + 7,       
    12.         .flags = IORESOURCE_MEM       
    13.     },       
    14.     [2] = {       
    15.         .start = IRQ_EINT7,       
    16.         .end   = IRQ_EINT7,       
    17.         .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,       
    18.     }       
    19. };    

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

    1. static struct dm9000_plat_data mini2440_dm9k_pdata __initdata = {      
    2.     .flags      = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),      
    3. };      
    4.      
    5. static struct platform_device mini2440_device_eth __initdata = {      
    6.     .name       = "dm9000",      
    7.     .id     = -1,      
    8.     .num_resources  = ARRAY_SIZE(mini2440_dm9k_resource),      
    9.     .resource   = mini2440_dm9k_resource,      
    10.     .dev        = {      
    11.         .platform_data  = &mini2440_dm9k_pdata,      
    12.     },      
    13. };    
    14.   
    15.    
    16. MACHINE_START(MINI2440, "MINI2440")      
    17.     /* Maintainer: Michel Pollet <buserror@gmail.com> */     
    18.     .phys_io    = S3C2410_PA_UART,      
    19.     .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,      
    20.     .boot_params    = S3C2410_SDRAM_PA + 0x100,      
    21.     .map_io     = mini2440_map_io,      
    22.     .init_machine   = mini2440_init, /*初始化函数*/     
    23.     .init_irq   = s3c24xx_init_irq,      
    24.     .timer      = &s3c24xx_timer,      
    25. MACHINE_END    
    26.      
    27.   
    28.   
    29.    
    30. static void __init mini2440_init(void)      
    31. {      
    32.     ...      
    33.          ...      
    34.     platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));      
    35.      
    36.         ...      
    37.         ...      
    38. }    
    39.   
    40.   
    41.    
    42. static struct platform_device *mini2440_devices[] __initdata = {      
    43.     &s3c_device_usb,      
    44.     &s3c_device_wdt,      
    45. /*  &s3c_device_adc,*/ /* ADC doesn't like living with touchscreen ! */     
    46.     &s3c_device_i2c0,      
    47.     &s3c_device_rtc,      
    48.     &s3c_device_usbgadget,      
    49.     &mini2440_device_eth, /*dm9000是众多平台设备中的一个*/     
    50.     &mini2440_led1,      
    51.     &mini2440_led2,      
    52.     &mini2440_led3,      
    53.     &mini2440_led4,      
    54.     &mini2440_button_device,      
    55.     &s3c_device_nand,      
    56.     &s3c_device_sdi,      
    57.     &s3c_device_iis,      
    58.     &mini2440_audio,      
    59. /*  &s3c_device_timer[0],*/ /* buzzer pwm, no API for it */     
    60.     /* remaining devices are optional */     
    61. };    

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

     *sk_buff

     

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

     

     *net_device

     

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

     

    三、具体代码的分析

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

     

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

    1. static struct platform_driver dm9000_driver = {      
    2.     .driver = {      
    3.         .name    = "dm9000",      
    4.         .owner   = THIS_MODULE,      
    5.     },      
    6.     .probe   = dm9000_probe,      
    7.     .remove  = __devexit_p(dm9000_drv_remove),      
    8.     .suspend = dm9000_drv_suspend,      
    9.     .resume  = dm9000_drv_resume,      
    10. };    

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

     

    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. };    


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


    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_eeprom_len     = dm9000_get_eeprom_len,      
    10.     .get_eeprom     = dm9000_get_eeprom,      
    11.     .set_eeprom     = dm9000_set_eeprom,      
    12. };    

    *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     dbug_cnt;      
    12.     u8      io_mode;        /* 0:word, 2:byte */     
    13.     u8      phy_addr;      
    14.     u8      imr_all;      
    15.      
    16.     unsigned int    flags;      
    17.     unsigned int    in_suspend :1;      
    18.     int     debug_level;      
    19.      
    20.     enum dm9000_type type;      
    21.      
    22.     void (*inblk)(void __iomem *port, void *data, int length);      
    23.     void (*outblk)(void __iomem *port, void *data, int length);      
    24.     void (*dumpblk)(void __iomem *port, int length);      
    25.      
    26.     struct device   *dev;        /* parent device */     
    27.      
    28.     struct resource *addr_res;   /* resources found */     
    29.     struct resource *data_res;      
    30.     struct resource *addr_req;   /* resources requested */     
    31.     struct resource *data_req;      
    32.     struct resource *irq_res;      
    33.      
    34.     struct mutex     addr_lock; /* phy and eeprom access lock */     
    35.      
    36.     struct delayed_work phy_poll;      
    37.     struct net_device  *ndev;      
    38.      
    39.     spinlock_t  lock;      
    40.      
    41.     struct mii_if_info mii;      
    42.     u32     msg_enable;      
    43. } board_info_t;  

    下面看一下具体代码。

     

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

     

        1. 注册平台驱动。

     

     

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

    1. static struct platform_driver dm9000_driver = {      
    2.     .driver = {      
    3.         .name    = "dm9000"/*用这个名字完成驱动和设备的match*/     
    4.         .owner   = THIS_MODULE,      
    5.     },      
    6.     .probe   = dm9000_probe,      
    7.     .remove  = __devexit_p(dm9000_drv_remove),      
    8.     .suspend = dm9000_drv_suspend,      
    9.     .resume  = dm9000_drv_resume,      
    10. };      
    11.      
    12. static int __init      
    13. dm9000_init(void)      
    14. {      
    15.     printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);      
    16.      
    17.     return platform_driver_register(&dm9000_driver);      
    18. }    

     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。

    下面是代码清单:

    1.    
    2. static int __devinit      
    3. dm9000_probe(struct platform_device *pdev)      
    4. {      
    5.     struct dm9000_plat_data *pdata = pdev->dev.platform_data;      
    6.     struct board_info *db;  /* Point a board information structure */     
    7.     struct net_device *ndev;      
    8.     const unsigned char *mac_src;      
    9.     int ret = 0;      
    10.     int iosize;      
    11.     int i;      
    12.     u32 id_val;      
    13.      
    14.     /* Init network device */     
    15.         /*使用alloc_etherdev()来生成一个net_device结构体,并对其公有成员赋值*/     
    16.     ndev = alloc_etherdev(sizeof(struct board_info));      
    17.     if (!ndev) {      
    18.         dev_err(&pdev->dev, "could not allocate device.\n");      
    19.         return -ENOMEM;      
    20.     }      
    21.      
    22.     SET_NETDEV_DEV(ndev, &pdev->dev);      
    23.      
    24.     dev_dbg(&pdev->dev, "dm9000_probe()\n");      
    25.      
    26.     /* setup board info structure */     
    27.     db = netdev_priv(ndev);      
    28.     memset(db, 0, sizeof(*db));      
    29.      
    30.     db->dev = &pdev->dev;      
    31.     db->ndev = ndev;      
    32.      
    33.     spin_lock_init(&db->lock);      
    34.     mutex_init(&db->addr_lock);      
    35.      
    36.     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);      
    37.      
    38.     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);      
    39.     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);      
    40.     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);      
    41.      
    42.     if (db->addr_res == NULL || db->data_res == NULL ||      
    43.         db->irq_res == NULL) {      
    44.         dev_err(db->dev, "insufficient resources\n");      
    45.         ret = -ENOENT;      
    46.         goto out;      
    47.     }      
    48.      
    49.     iosize = res_size(db->addr_res);      
    50.     db->addr_req = request_mem_region(db->addr_res->start, iosize,      
    51.                       pdev->name);      
    52.      
    53.     if (db->addr_req == NULL) {      
    54.         dev_err(db->dev, "cannot claim address reg area\n");      
    55.         ret = -EIO;      
    56.         goto out;      
    57.     }      
    58.      
    59.     db->io_addr = ioremap(db->addr_res->start, iosize);      
    60.      
    61.     if (db->io_addr == NULL) {      
    62.         dev_err(db->dev, "failed to ioremap address reg\n");      
    63.         ret = -EINVAL;      
    64.         goto out;      
    65.     }      
    66.      
    67.     iosize = res_size(db->data_res);      
    68.     db->data_req = request_mem_region(db->data_res->start, iosize,      
    69.                       pdev->name);      
    70.      
    71.     if (db->data_req == NULL) {      
    72.         dev_err(db->dev, "cannot claim data reg area\n");      
    73.         ret = -EIO;      
    74.         goto out;      
    75.     }      
    76.      
    77.     db->io_data = ioremap(db->data_res->start, iosize);      
    78.      
    79.     if (db->io_data == NULL) {      
    80.         dev_err(db->dev, "failed to ioremap data reg\n");      
    81.         ret = -EINVAL;      
    82.         goto out;      
    83.     }      
    84.      
    85.     /* fill in parameters for net-dev structure */     
    86.     ndev->base_addr = (unsigned long)db->io_addr;      
    87.     ndev->irq    = db->irq_res->start;      
    88.      
    89.     /* ensure at least we have a default set of IO routines */     
    90.     dm9000_set_io(db, iosize);      
    91.      
    92.     /* check to see if anything is being over-ridden */     
    93.     if (pdata != NULL) {      
    94.         /* check to see if the driver wants to over-ride the    
    95.          * default IO width */     
    96.      
    97.         if (pdata->flags & DM9000_PLATF_8BITONLY)      
    98.             dm9000_set_io(db, 1);      
    99.      
    100.         if (pdata->flags & DM9000_PLATF_16BITONLY)      
    101.             dm9000_set_io(db, 2);      
    102.      
    103.         if (pdata->flags & DM9000_PLATF_32BITONLY)      
    104.             dm9000_set_io(db, 4);      
    105.      
    106.         /* check to see if there are any IO routine    
    107.          * over-rides */     
    108.      
    109.         if (pdata->inblk != NULL)      
    110.             db->inblk = pdata->inblk;      
    111.      
    112.         if (pdata->outblk != NULL)      
    113.             db->outblk = pdata->outblk;      
    114.      
    115.         if (pdata->dumpblk != NULL)      
    116.             db->dumpblk = pdata->dumpblk;      
    117.      
    118.         db->flags = pdata->flags;      
    119.     }     
    120.     
    121. #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL      
    122.     db->flags |= DM9000_PLATF_SIMPLE_PHY;     
    123. #endif      
    124.      
    125.     dm9000_reset(db);      
    126.      
    127.     /* try multiple times, DM9000 sometimes gets the read wrong */     
    128.     for (i = 0; i < 8; i++) {      
    129.         id_val  = ior(db, DM9000_VIDL);      
    130.         id_val |= (u32)ior(db, DM9000_VIDH) << 8;      
    131.         id_val |= (u32)ior(db, DM9000_PIDL) << 16;      
    132.         id_val |= (u32)ior(db, DM9000_PIDH) << 24;      
    133.      
    134.         if (id_val == DM9000_ID)      
    135.             break;      
    136.         dev_err(db->dev, "read wrong id 0x%08x\n", id_val);      
    137.     }      
    138.      
    139.     if (id_val != DM9000_ID) {      
    140.         dev_err(db->dev, "wrong id: 0x%08x\n", id_val);      
    141.         ret = -ENODEV;      
    142.         goto out;      
    143.     }      
    144.      
    145.     /* Identify what type of DM9000 we are working on */     
    146.      
    147.     id_val = ior(db, DM9000_CHIPR);      
    148.     dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);      
    149.      
    150.     switch (id_val) {      
    151.     case CHIPR_DM9000A:      
    152.         db->type = TYPE_DM9000A;      
    153.         break;      
    154.     case CHIPR_DM9000B:      
    155.         db->type = TYPE_DM9000B;      
    156.         break;      
    157.     default:      
    158.         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);      
    159.         db->type = TYPE_DM9000E;      
    160.     }      
    161.      
    162.     /* from this point we assume that we have found a DM9000 */     
    163.      
    164.     /* driver system function */     
    165.     ether_setup(ndev);      
    166.      
    167.     ndev->netdev_ops = &dm9000_netdev_ops;      
    168.     ndev->watchdog_timeo = msecs_to_jiffies(watchdog);      
    169.     ndev->ethtool_ops    = &dm9000_ethtool_ops;      
    170.      
    171.     db->msg_enable       = NETIF_MSG_LINK;      
    172.     db->mii.phy_id_mask  = 0x1f;      
    173.     db->mii.reg_num_mask = 0x1f;      
    174.     db->mii.force_media  = 0;      
    175.     db->mii.full_duplex  = 0;      
    176.     db->mii.dev       = ndev;      
    177.     db->mii.mdio_read    = dm9000_phy_read;      
    178.     db->mii.mdio_write   = dm9000_phy_write;      
    179.      
    180.     mac_src = "eeprom";      
    181.      
    182.     /* try reading the node address from the attached EEPROM */     
    183.     for (i = 0; i < 6; i += 2)      
    184.         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);      
    185.      
    186.     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {      
    187.         mac_src = "platform data";      
    188.         memcpy(ndev->dev_addr, pdata->dev_addr, 6);      
    189.     }      
    190.      
    191.     if (!is_valid_ether_addr(ndev->dev_addr)) {      
    192.         /* try reading from mac */     
    193.               
    194.         mac_src = "chip";      
    195.         for (i = 0; i < 6; i++)      
    196.             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);      
    197.     }      
    198.      
    199.     if (!is_valid_ether_addr(ndev->dev_addr))      
    200.         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "     
    201.              "set using ifconfig\n", ndev->name);      
    202.      
    203.     platform_set_drvdata(pdev, ndev);      
    204.     ret = register_netdev(ndev);      
    205.      
    206.     if (ret == 0)      
    207.         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",      
    208.                ndev->name, dm9000_type_to_char(db->type),      
    209.                db->io_addr, db->io_data, ndev->irq,      
    210.                ndev->dev_addr, mac_src);      
    211.     return 0;      
    212.      
    213. out:      
    214.     dev_err(db->dev, "not found (%d).\n", ret);      
    215.      
    216.     dm9000_release_board(pdev, db);      
    217.     free_netdev(ndev);      
    218.      
    219.     return ret;      
    220. }    
    展开全文
  • mini2440 dm9000 网卡驱动详解 (二)

    千次阅读 2013-07-31 19:26:42
    mini2440 dm9000 网卡驱动详解 (二) 3. platform_driver的remove, suspend和resume的实现  remove函数的功能是把设备从内核中移除,释放内存区域。该函数在卸载模块时被调用。代码清单如下:   ...
  • *dm9000_get_drvinfo()    该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool_drvinfo结构体变量。代码清单如下:     [cpp] view plaincopy static void ...
  • 3. platform_driver的remove, suspend和resume的实现  remove函数的功能是把设备从内核中移除,释放内存区域。该函数在卸载模块时被调用。代码清单如下:   ...dm9000_drv_remove(s
  • dm9000 设备驱动详解 mini2440
  • *dm9000_get_drvinfo() 该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool_drvinfo结构体变量。代码清单如下: *dm9000_get_drvinfo() 该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool...
  • 3. platform_driver的remove, suspend和resume的实现 remove函数的功能是把设备从内核中移除,释放内存区域。该函数在卸载模块时被调用。...dm9000_drv_remove(struct platform_device *pdev) { struct net_de...
  • 通过模块的加载函数看出DM9000A的驱动是以平台驱动的形式注册进内核的,下边是模块的加载函数: static   int  __init dm9000_init( void ) {  printk(KERN_INFO  "%s Ethernet Driver, V%s\n" ,...
  • 接下来本节,学习网卡芯片DM9000C,如何编写移植DM9000C网卡驱动程序。 1.首先来看DM9000C原理图 如下图所示:  (#:表示低电平有效) SD0~15:16位数据线,有CMD引脚决定访问类型 CMD: 命令线,当CMD为高,表示...
  • 上一节 我们学习了: ...接下来本节,学习网卡芯片DM9000C,如何编写移植DM9000C网卡驱动程序。 1.首先来看DM9000C原理图 如下图所示:  (#:表示低电平有效) SD0~15:16位数据线,有CMD引脚决定访问...
  • 网卡驱动设计分析 DM9000网卡驱动位于内核源代码的drivers/net/dm9000.c中,它基于平台驱动架构,代码清单14.19抽取了它的主干。其核心工作是实现了前文所述net_device结构体中的hard_start_xmit()、open()、...
  • 网卡设备驱动实例 14.9.1 DM9000网卡硬件描述 DM9000是开发板采用的网络芯片,是一个高度集成且功耗很低的高速网络控制器,可以和CPU直连,支持10/100MB以太网连接,芯片内部自带4KB双字节的SRAM(3KB用来发送,13...
  • Linux内核驱动模块的删除

    千次阅读 2017-05-16 22:33:39
    今天看了《Linux设备驱动开发详解》的第四章模块和第五章文件。...首先,我从文件中找到DM9000驱动模块所在位置,在~/drives/net下。于是,我从Linux内核根目录下查看makefile,逐级向下直到找到net文件夹下的ma
  • 网上也都说了,ok6410的uboot里面可恶的网卡驱动是cs8900的,而实际网卡是dm9000的,所以导致进入uboot后就没有办法用tftp来下载内核了。不过有人已经实现了这个功能。 下面分几个小标题,各个实现各个说明  ...
  • 网上也都说了,ok6410的uboot里面可恶的网卡驱动是cs8900的,而实际网卡是dm9000的,所以导致进入uboot后就没有办法用tftp来下载内核了。不过有人已经实现了这个功能。 下面分几个小标题,各个实现各个说明
  • 嵌入式Linux之我行系列

    热门讨论 2011-01-28 15:41:21
    ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之DM9000E网卡驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之USB驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之MMC/SD卡驱动 ·...

空空如也

空空如也

1 2
收藏数 27
精华内容 10
热门标签
关键字:

dm9000驱动详解