首页 > 系统相关 >linux驱动移植-DM9000网卡驱动

linux驱动移植-DM9000网卡驱动

时间:2023-02-02 22:44:17浏览次数:63  
标签:platform DM9000 db dev 网卡 dm9000 驱动 ndev

在学习Mini2440裸机程序时,我们介绍过关于DM9000网卡的相关知识,包括电路图、以及DM9000寄存器等信息。具体可以参考Mini2440裸机开发之DM9000

本节对之前已经介绍过的知识不会再进行重复介绍。这一节我们将直入主题,介绍如何移植DM9000网卡驱动。

一、platform设备注册(dm9000)

在刚学习驱动移植的时候,我们为了使用nfs作为根文件系统,我们在linux驱动移植-DM9000网卡驱动小节介绍了DM9000网卡驱动的移植,但是那时候我们仅仅是移植,并为对源码进行深入研究。

DM9000网卡设备驱动,其采用的也是platform设备驱动模型。

1.1 smdk2440_device_eth

我们定位到arch/arm/mach-s3c24xx/mach-smdk2440.c文件,在该文件中我们引入了dm9000.h头文件:

#include <linux/dm9000.h>

定义了DM9000网卡设备的物理基地址:

#define MACH_SMDK2440_DM9K_BASE (S3C2410_CS4 + 0x300)     # S3C2410_CS4 = 0X20000000

定义了网卡platform设备smdk2440_device_eth:

/* DM9000AEP 10/100 ethernet controller */

static struct resource smdk2440_dm9k_resource[] = {
        [0] = DEFINE_RES_MEM(MACH_SMDK2440_DM9K_BASE, 4),
        [1] = DEFINE_RES_MEM(MACH_SMDK2440_DM9K_BASE + 4, 4),
        [2] = DEFINE_RES_NAMED(IRQ_EINT7, 1, NULL, IORESOURCE_IRQ
                                                | IORESOURCE_IRQ_HIGHEDGE),
};

/*
 * The DM9000 has no eeprom, and it's MAC address is set by
 * the bootloader before starting the kernel.
 */
static struct dm9000_plat_data smdk2440_dm9k_pdata = {
        .flags          = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};

static struct platform_device smdk2440_device_eth = {
        .name           = "dm9000",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(smdk2440_dm9k_resource),
        .resource       = smdk2440_dm9k_resource,
        .dev            = {
                .platform_data  = &smdk2440_dm9k_pdata,
        },
};

1.2  smdk2440_machine_init

linux内核启动的时候会根据uboot中设置的机器id执行相应的初始化工作,比如.init_machine、.init_irq,我们首先定位到arch/arm/mach-s3c24xx/mach-smdk2440.c:

MACHINE_START(S3C2440, "SMDK2440")
        /* Maintainer: Ben Dooks <[email protected]> */
        .atag_offset    = 0x100,

        .init_irq       = s3c2440_init_irq,
        .map_io         = smdk2440_map_io,
        .init_machine   = smdk2440_machine_init,
        .init_time      = smdk2440_init_time,
MACHINE_END

重点关注init_machine,init_machine中保存的是开发板资源注册的初始化代码。

static void __init smdk2440_machine_init(void)
{
        s3c24xx_fb_set_platdata(&smdk2440_fb_info);   
        s3c_i2c0_set_platdata(NULL);

        platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));  // s3c2440若干个platform设备注册 usb host controller、lcd、wdt等
        smdk_machine_init();  // s3c24x0系列若干个platform设备注册(通用)
}

这里利用platform_add_devices进行若干个platform设备的注册,该函数还是通过调用platform_device_register实现platform设备注册.

static struct platform_device *smdk2440_devices[] __initdata = {
        &s3c_device_ohci,
        &s3c_device_lcd,
        &s3c_device_wdt,
        &s3c_device_i2c0,
        &s3c_device_iis,
        &smdk2440_device_eth,
};

二、platform驱动注册dm9000)

我们需要配置内核支持DM9000网卡驱动:

Device Drivers --->
   [*] Network device support -->
       [*] Ethernet driver support -->
[*] DM9000 support

这样我们内核才会支持DM9000网卡驱动。配置了DM9000 support之后,配置文件.config中会包含如下项:

# CONFIG_GEMINI_ETHERNET is not set
CONFIG_DM9000=y

当我们使用make uImage编译内核时会将dm9000.o编译进内核:

drivers/net/ethernet/davicom/Makefile:6:obj-$(CONFIG_DM9000) += dm9000.o

dm9000.c文件位于drivers/net/ethernet/davicom目录下。

2.1 入口和出口函数

我们在dm9000.c文件定位到驱动模块的入口和出口:

module_platform_driver(dm9000_driver);

module_platform_driver宏展开后本质上就是:

module_init(dm9000_driver_init); 
module_exit(dm9000_driver_exit); 
static int __init dm9000_driver_init(void)
{
     platform_driver_register(dm9000_driver);
}

static void __exit dm9000_driver_exit(void)
{
     platform_driver_unregister(dm9000_driver);
}

看到这里是不是有点意外,这里是通过platform_driver_register函数注册了一个platform驱动。

在plaftrom总线设备驱动模型中,我们知道当内核中有platform设备platform驱动匹配,会调用到platform_driver里的成员.probe,在这里就是dm9000_probe函数。

static struct platform_driver dm9000_driver = {
        .driver = {
                .name    = "dm9000",
                .pm      = &dm9000_drv_pm_ops,
                .of_match_table = of_match_ptr(dm9000_of_matches),
        },
        .probe   = dm9000_probe,                                              
        .remove  = dm9000_drv_remove,
};

2.2 dm9000_probe

/*
 * Search DM9000 board, allocate space and register it
 */
static int
dm9000_probe(struct platform_device *pdev)
{
        struct dm9000_plat_data *pdata = dev_get_platdata(&pdev->dev);   // pdev->dev.platform_data
        struct board_info *db;  /* Point a board information structure */
        struct net_device *ndev;
        struct device *dev = &pdev->dev;
        const unsigned char *mac_src;
        int ret = 0;
        int iosize;
        int i;
        u32 id_val;
        int reset_gpios;
        enum of_gpio_flags flags;
        struct regulator *power;
        bool inv_mac_addr = false;

        power = devm_regulator_get(dev, "vcc");
        if (IS_ERR(power)) {
                if (PTR_ERR(power) == -EPROBE_DEFER)
                        return -EPROBE_DEFER;
                dev_dbg(dev, "no regulator provided\n");
        } else {
                ret = regulator_enable(power);
                if (ret != 0) {
                        dev_err(dev,
                                "Failed to enable power regulator: %d\n", ret);
                        return ret;
                }
                dev_dbg(dev, "regulator enabled\n");
        }

        reset_gpios = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0,
                                              &flags);
        if (gpio_is_valid(reset_gpios)) {
                ret = devm_gpio_request_one(dev, reset_gpios, flags,
                                            "dm9000_reset");
                if (ret) {
                        dev_err(dev, "failed to request reset gpio %d: %d\n",
                                reset_gpios, ret);
                        return -ENODEV;
                }

                /* According to manual PWRST# Low Period Min 1ms */
                msleep(2);
                gpio_set_value(reset_gpios, 1);
                /* Needs 3ms to read eeprom when PWRST is deasserted */
                msleep(4);
        }

        if (!pdata) {
                pdata = dm9000_parse_dt(&pdev->dev);
                if (IS_ERR(pdata))
                        return PTR_ERR(pdata);
        }

        /* Init network device */
        ndev = alloc_etherdev(sizeof(struct board_info));
        if (!ndev)
                return -ENOMEM;

        SET_NETDEV_DEV(ndev, &pdev->dev);

        dev_dbg(&pdev->dev, "dm9000_probe()\n");

        /* setup board info structure */
        db = netdev_priv(ndev);

        db->dev = &pdev->dev;
        db->ndev = ndev;

        spin_lock_init(&db->lock);
        mutex_init(&db->addr_lock);

        INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);

        db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);

        if (!db->addr_res || !db->data_res) {
                dev_err(db->dev, "insufficient resources addr=%p data=%p\n",
                        db->addr_res, db->data_res);
                ret = -ENOENT;
                goto out;
        }

        ndev->irq = platform_get_irq(pdev, 0);
        if (ndev->irq < 0) {
                dev_err(db->dev, "interrupt resource unavailable: %d\n",
                        ndev->irq);
                ret = ndev->irq;
                goto out;
        }

        db->irq_wake = platform_get_irq(pdev, 1);
        if (db->irq_wake >= 0) {
                dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);

                ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
                                  IRQF_SHARED, dev_name(db->dev), ndev);
                if (ret) {
                        dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);
                } else {

                        /* test to see if irq is really wakeup capable */
                        ret = irq_set_irq_wake(db->irq_wake, 1);
                        if (ret) {
                                dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",
                                        db->irq_wake, ret);
                                ret = 0;
                        } else {
                                irq_set_irq_wake(db->irq_wake, 0);
                                db->wake_supported = 1;
                        }
                }
        }

        iosize = resource_size(db->addr_res);
        db->addr_req = request_mem_region(db->addr_res->start, iosize,
                                          pdev->name);

        if (db->addr_req == NULL) {
                dev_err(db->dev, "cannot claim address reg area\n");
                ret = -EIO;
                goto out;
        }

        db->io_addr = ioremap(db->addr_res->start, iosize);

        if (db->io_addr == NULL) {
                dev_err(db->dev, "failed to ioremap address reg\n");
                ret = -EINVAL;
                goto out;
        }

        iosize = resource_size(db->data_res);
        db->data_req = request_mem_region(db->data_res->start, iosize,
                                          pdev->name);

        if (db->data_req == NULL) {
                dev_err(db->dev, "cannot claim data reg area\n");
                ret = -EIO;
                goto out;
        }

        db->io_data = ioremap(db->data_res->start, iosize);

        if (db->io_data == NULL) {
                dev_err(db->dev, "failed to ioremap data reg\n");
                ret = -EINVAL;
                goto out;
        }

        /* fill in parameters for net-dev structure */
        ndev->base_addr = (unsigned long)db->io_addr;

        /* ensure at least we have a default set of IO routines */
        dm9000_set_io(db, iosize);

        /* check to see if anything is being over-ridden */
        if (pdata != NULL) {
                /* check to see if the driver wants to over-ride the
                 * default IO width */

                if (pdata->flags & DM9000_PLATF_8BITONLY)
                        dm9000_set_io(db, 1);

                if (pdata->flags & DM9000_PLATF_16BITONLY)
                        dm9000_set_io(db, 2);

                if (pdata->flags & DM9000_PLATF_32BITONLY)
                        dm9000_set_io(db, 4);

                /* check to see if there are any IO routine
                 * over-rides */

                if (pdata->inblk != NULL)
                        db->inblk = pdata->inblk;

                if (pdata->outblk != NULL)
                        db->outblk = pdata->outblk;

                if (pdata->dumpblk != NULL)
                        db->dumpblk = pdata->dumpblk;

                db->flags = pdata->flags;
        }

#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
        db->flags |= DM9000_PLATF_SIMPLE_PHY;
#endif

        dm9000_reset(db);

        /* try multiple times, DM9000 sometimes gets the read wrong */
        for (i = 0; i < 8; i++) {
                id_val  = ior(db, DM9000_VIDL);
                id_val |= (u32)ior(db, DM9000_VIDH) << 8;
                id_val |= (u32)ior(db, DM9000_PIDL) << 16;
                id_val |= (u32)ior(db, DM9000_PIDH) << 24;

                if (id_val == DM9000_ID)
                        break;
                dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
        }

        if (id_val != DM9000_ID) {
                dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
                ret = -ENODEV;
                goto out;
        }

        /* Identify what type of DM9000 we are working on */

        id_val = ior(db, DM9000_CHIPR);
        dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);

        switch (id_val) {
        case CHIPR_DM9000A:
                db->type = TYPE_DM9000A;
                break;
        case CHIPR_DM9000B:
                db->type = TYPE_DM9000B;
                break;
        default:
                dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);
                db->type = TYPE_DM9000E;
        }

        /* dm9000a/b are capable of hardware checksum offload */
        if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
                ndev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
                ndev->features |= ndev->hw_features;
        }

        /* from this point we assume that we have found a DM9000 */

        ndev->netdev_ops        = &dm9000_netdev_ops;
        ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);
        ndev->ethtool_ops       = &dm9000_ethtool_ops;

        db->msg_enable       = NETIF_MSG_LINK;
        db->mii.phy_id_mask  = 0x1f;
        db->mii.reg_num_mask = 0x1f;
        db->mii.force_media  = 0;
        db->mii.full_duplex  = 0;
        db->mii.dev          = ndev;
        db->mii.mdio_read    = dm9000_phy_read;
        db->mii.mdio_write   = dm9000_phy_write;

        mac_src = "eeprom";

        /* try reading the node address from the attached EEPROM */
        for (i = 0; i < 6; i += 2)
                dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);

        if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
                mac_src = "platform data";
                memcpy(ndev->dev_addr, pdata->dev_addr, ETH_ALEN);
        }

        if (!is_valid_ether_addr(ndev->dev_addr)) {
                /* try reading from mac */

                mac_src = "chip";
                for (i = 0; i < 6; i++)
                        ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
        }

        if (!is_valid_ether_addr(ndev->dev_addr)) {
                inv_mac_addr = true;
                eth_hw_addr_random(ndev);
                mac_src = "random";
        }


        platform_set_drvdata(pdev, ndev);
        ret = register_netdev(ndev);

        if (ret == 0) {
                if (inv_mac_addr)
                        dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please set using ip\n",
                                 ndev->name);
                printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
                       ndev->name, dm9000_type_to_char(db->type),
                       db->io_addr, db->io_data, ndev->irq,
                       ndev->dev_addr, mac_src);
        }
        return 0;

out:
        dev_err(db->dev, "not found (%d).\n", ret);

        dm9000_release_board(pdev, db);
        free_netdev(ndev);

        return ret;
}

 这段代码属实有点长了,让人一眼看过去,就有点想放弃去读的想法,既然都学习到了这一步,我们还是耐着性去分析吧。

三、代码下载

Young / s3c2440_project[drivers]

参考文章:

[1] 移植DM900C网卡驱动

[2]二十、Linux驱动之移植DM9000C网卡驱动(上)

[3]二十一、Linux驱动之移植DM9000C网卡驱动(下)

[4]27.Linux-DM9000C网卡移植(详解)

[5]Linux驱动之网卡驱动剖析

 

标签:platform,DM9000,db,dev,网卡,dm9000,驱动,ndev
From: https://www.cnblogs.com/zyly/p/17087631.html

相关文章

  • debian 显卡驱动
    https://wiki.debian.org/AtiHowTo 01:00.0VGAcompatiblecontroller[0300]:AdvancedMicroDevices,Inc.[AMD/ATI]BartsPRO[RadeonHD6850][1002:6739]amd......
  • Linux ALSA驱动之二:声卡的创建流程
    1、structsnd_card1.1、snd_card是啥snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card......
  • MT8788 android 9.0 sensor 驱动移植
    1.硬件配置打开 MT8788 核心板原理图,配置GYRO(陀螺仪),ALSPS(光感),G-sensor(加速度)的ENIT和GPIO以及I2C,打开vendor\mediatek\proprietary\scripts\dct\DrvGen.......
  • USB键盘驱动编写和测试
    一、原理分析1.首先通过打印usb_buf[i]中的8字节数据,看一下按键按下之后会接收到什么。1)通过按完所有键盘按键打印的结果可知,有8个按键会打印在usb_buf[0]里,即:ctrl左键......
  • VK1S68C/VK1640B是血氧仪LED数码管显示驱动芯片/LED数显驱动控制电路(IC),SSOP24小体积封
    产品品牌:永嘉微电/VINKA产品型号:VK1S68C封装形式:SSOP24概述:VK1S68C是一种带键盘扫描接口的数码管或点阵LED驱动控制专用芯片,内部集成有3线串行接口、数据锁存器、LED......
  • OPNsense调整网卡RSS参数提升网络性能
    RSS用于使用散列函数在CPU内核上分发数据包——为卸载散列的硬件或软件提供支持。启用接收方缩放(RSS)时,特定TCP连接的所有接收数据处理都在多个处理器或处理器内核之间......
  • 血氧仪/胎心仪等低功耗小体积数码管显示屏LED数显驱动IC-VK1S68C【FAE技术支持】
    VK1S68C是1/5~1/8占空比的LED显示控制驱动电路。由10根段输出、4根栅输出、3根段/栅输出,1个显示存储器、控制电路、键扫描电路组成了一个高可靠性的单片机外围LE......
  • Linux下的硬件驱动——USB设备
    USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题。本文着力从Linux系统下设备驱动的架构,去阐述......
  • 使用EB配置PWM驱动
    概述脉宽调制(PWM)驱动器负责提供与AUTOSAR指定的PWM信号生成相关的标准服务。PWM通道的底层定时器引擎是一个GTM (TOM或ATOM片)或CCU6 (T12或T13片)定时器通道。......
  • STM32的FSMC地址线对应关系通俗易懂解读和和驱动TFT-LCD的原理
    STM32的FSMC地址线对应关系通俗易懂解读和和驱动TFT-LCD的原理当Bank接的是8位宽度存储器的时候:HADDR[25:0]对应FSMC_A[25:0]当Bank接的是16位宽度存储器的时候:HADDR......