ARM-Linux驱动--DM9000网卡驱动分析(二)

发布者:xi24最新更新时间:2016-04-27 来源: eefocus关键字:ARM  Linux驱动  DM9000  网卡驱动 手机看文章 扫描二维码
随时随地手机看文章
硬件平台:FL2440(s3c2440)
内核版本:2.6.35
主机平台:Ubuntu 11.04
内核版本:2.6.39
原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/6612623
下面开始分析具体的代码,这里由于使DM9000驱动更容易理解,在不影响基本的功能的前提下,这里将尽可能的简化该驱动(如:去掉该驱动中支持电源管理的功能)
分析该驱动
1、首先看一下该驱动的平台设备驱动的结构体定义
view plainprint?
 
static struct platform_driver dm9000_driver = {  
    .driver = {  
        .name    = "dm9000",  
        .owner   = THIS_MODULE,  
    },  
    .probe   = dm9000_probe,  
    .remove  = __devexit_p(dm9000_drv_remove),  
};  
在执行insmod后内核自动那个执行下面的函数
view plainprint?
static int __init  
dm9000_init(void)  
{  
    printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);  
  
    return platform_driver_register(&dm9000_driver);  
}  
调用函数platform_driver_register()函数注册驱动。
3、自动执行驱动的probe函数,进行资源的探测和申请资源。
其中BWSCON为总线宽度 等待控制寄存器
其中第[19:18]位的作用如下
下面函数中将两位设置为11,也就是WAIT使能,bank4使用UB/LB。
alloc_etherdev()函数分配一个网络设备的结构体,原型在include/linux/etherdevice.h
原型如下:
view plainprint?
extern struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count);  
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)  
该函数中需要将获得的资源信息存储在一个结构体中,定义如下:
view plainprint?
 
typedef struct board_info {  
  
    void __iomem    *io_addr;    
    void __iomem    *io_data;    
    u16      irq;        
  
    u16     tx_pkt_cnt;  
    u16     queue_pkt_len;  
    u16     queue_start_addr;  
    u16     queue_ip_summed;  
    u16     dbug_cnt;  
    u8      io_mode;          
    u8      phy_addr;  
    u8      imr_all;  
  
    unsigned int    flags;  
    unsigned int    in_suspend :1;  
    unsigned int    wake_supported :1;  
    int     debug_level;  
  
    enum dm9000_type type;  
  
    void (*inblk)(void __iomem *port, void *data, int length);  
    void (*outblk)(void __iomem *port, void *data, int length);  
    void (*dumpblk)(void __iomem *port, int length);  
  
    struct device   *dev;          
  
    struct resource *addr_res;    
    struct resource *data_res;  
    struct resource *addr_req;    
    struct resource *data_req;  
    struct resource *irq_res;  
  
    int      irq_wake;  
  
    struct mutex     addr_lock;  
  
    struct delayed_work phy_poll;  
    struct net_device  *ndev;  
  
    spinlock_t  lock;  
  
    struct mii_if_info mii;  
    u32     msg_enable;  
    u32     wake_state;  
  
    int     rx_csum;  
    int     can_csum;  
    int     ip_summed;  
} board_info_t;  
下面是probe函数,
其中有个函数db = netdev_priv(ndev)
该函数实际上是返回网卡私有成员的数据结构地址
函数如下,定义在include/linux/net_device.h中
view plainprint?
static inline void *netdev_priv(const struct net_device *dev)  
{  
    return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);  
}  
view plainprint?
 
static int __devinit  
dm9000_probe(struct platform_device *pdev)  
{  
    struct dm9000_plat_data *pdata = pdev->dev.platform_data;  
    struct board_info *db;    
    struct net_device *ndev;  
    const unsigned char *mac_src;  
    int ret = 0;  
    int iosize;  
    int i;  
    u32 id_val;  
  
    unsigned char ne_def_eth_mac_addr[]={0x00,0x12,0x34,0x56,0x80,0x49};  
    static void *bwscon;  
    static void *gpfcon;  
    static void *extint0;  
    static void *intmsk;  
     
    #define BWSCON           (0x48000000)  
    #define GPFCON           (0x56000050)  
    #define EXTINT0           (0x56000088)  
    #define INTMSK           (0x4A000008)  
  
    bwscon=ioremap_nocache(BWSCON,0x0000004);  
    gpfcon=ioremap_nocache(GPFCON,0x0000004);  
    extint0=ioremap_nocache(EXTINT0,0x0000004);  
    intmsk=ioremap_nocache(INTMSK,0x0000004);  
  
    writel( readl(bwscon)|0xc0000,bwscon);  
    writel( (readl(gpfcon) & ~(0x3 << 14)) | (0x2 << 14), gpfcon);  
    writel( readl(gpfcon) | (0x1 << 7), gpfcon); // Disable pull-up,不使能上拉  
    writel( (readl(extint0) & ~(0xf << 28)) | (0x4 << 28), extint0); //rising edge,设置上升沿触发中断  
    writel( (readl(intmsk))  & ~0x80, intmsk);  
          
     
     
     
    ndev = alloc_etherdev(sizeof(struct board_info));  
    if (!ndev) {  
        dev_err(&pdev->dev, "could not allocate device.\n");  
        return -ENOMEM;  
    }  
  
 
    SET_NETDEV_DEV(ndev, &pdev->dev);  
  
    dev_dbg(&pdev->dev, "dm9000_probe()\n");  
  
     
     
    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);  
    db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  
    if (db->addr_res == NULL || db->data_res == NULL ||  
        db->irq_res == NULL) {  
        dev_err(db->dev, "insufficient resources\n");  
        ret = -ENOENT;  
        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 {  
  
             
            ret = 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 {  
                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;  
    }  
     
      
     
    ndev->base_addr = (unsigned long)db->io_addr;  
    ndev->irq    = db->irq_res->start;  
  
     
    dm9000_set_io(db, iosize);  
  
     
      
     
    if (pdata != NULL) {  
         
  
        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);  
  
         
  
        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);  
  
     
    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 0xx\n", id_val);  
    }  
  
    if (id_val != DM9000_ID) {  
        dev_err(db->dev, "wrong id: 0xx\n", id_val);  
        ret = -ENODEV;  
        goto out;  
    }  
  
     
  
    id_val = ior(db, DM9000_CHIPR);  
    dev_dbg(db->dev, "dm9000 revision 0xx\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 x => defaulting to DM9000E\n", id_val);  
        db->type = TYPE_DM9000E;  
    }  
  
     
    if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {  
        db->can_csum = 1;  
        db->rx_csum = 1;  
        ndev->features |= NETIF_F_IP_CSUM;  
    }  
  
     
  
     
    ether_setup(ndev);  
  
    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";  
  
     
    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, 6);  
    }  
  
    if (!is_valid_ether_addr(ndev->dev_addr)) {  
         
          
        mac_src = "chip";  
        for (i = 0; i < 6; i++)  
            ndev->dev_addr[i] = ne_def_eth_mac_addr[i];  
    }  
  
    if (!is_valid_ether_addr(ndev->dev_addr))  
        dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "  
             "set using ifconfig\n", ndev->name);  
  
     
    platform_set_drvdata(pdev, ndev);  
      
     
    ret = register_netdev(ndev);  
  
    if (ret == 0)  
        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;  
}  
这样,最后完成了网络设备的数据保存到总线上,将网络设备注册到内核。
4、设备的移除函数
view plainprint?
 
static int __devexit  
dm9000_drv_remove(struct platform_device *pdev)  
{  
    struct net_device *ndev = platform_get_drvdata(pdev);  
  
    platform_set_drvdata(pdev, NULL);  
  
    unregister_netdev(ndev);  
    dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));  
    free_netdev(ndev);        
  
    dev_dbg(&pdev->dev, "released and freed device\n");  
    return 0;  
}  
关键字:ARM  Linux驱动  DM9000  网卡驱动 引用地址:ARM-Linux驱动--DM9000网卡驱动分析(二)

上一篇:ARM-Linux驱动--DM9000网卡驱动分析(三)
下一篇:ARM-Linux驱动--DM9000网卡驱动分析(一)

推荐阅读最新更新时间:2024-03-16 14:52

基于DSP+ARM的便携式电能质量分析仪设计
  随着国家工业规模的扩大和科学技术的发展,电网负荷结构发生了很大的变化,一方面,非线性、冲击性和不平衡负荷的大量增长使得电能质量恶化;另一方面,随着信息技术的发展。越来越多的敏感负载对电能质量的要求也越来越高。这就要求电能质量检测分析设备具有实时检测、快速分析、实时显示的能力。采用高性能数字信号处理器( DSP )和嵌入式计算机系统( ARM )双处理器架构设计电能质量分析仪能满足上述要求。 DSP 系统实现电压、电流信号的实时采集处理,通过加窗傅里叶变换和小波算法得到电能质量参数; ARM 嵌入式平台运行WinCE操作系统完成人机交互、数据存储、实时显示等功能。该系统为仪器的可扩展性和智能化建立了良好的软硬件平
[嵌入式]
基于DSP+<font color='red'>ARM</font>的便携式电能质量分析仪设计
基于tiny4412的Linux内核移植 -- eMMC驱动移植(六)
平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本:Linux-4.4.0 (支持device tree) u-boot版本:友善之臂自带的 U-Boot 2010.12 (为支持uImage启动,做了少许改动) busybox版本:busybox 1.25 eMMC:KLMxGxFE3x-x00x 交叉编译工具链: arm-none-linux-gnueabi-gcc (gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29)) 概述 eMMC驱动在内核中也已经支持好了,代码在:
[单片机]
基于tiny4412的<font color='red'>Linux</font>内核移植 -- eMMC<font color='red'>驱动</font>移植(六)
基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读
本文将介绍看门狗驱动的实现。 目标平台:TQ2440 CPU:s3c2440 内核版本:2.6.30 1. 看门狗概述 看门狗其实就是一个定时器,当该定时器溢出前必须对看门狗进行 喂狗“,如果不这样做,定时器溢出后则将复位CPU。 因此,看门狗通常用于对处于异常状态的CPU进行复位。 具体的概念请自行百度。 2. S3C2440看门狗 s3c2440的看门狗的原理框图如下: 可以看出,看门狗定时器的频率由PCLK提供,其预分频器最大取值为255+1;另外,通过MUX,可以进一步降低频率。 定时器采用递减模式,一旦到0,则可以触发看门狗中断以及RESET复位信号。 看门狗定时器的频率的计算公式如下
[单片机]
基于S3C2440的嵌入式<font color='red'>Linux</font><font color='red'>驱动</font>——看门狗(watchdog)<font color='red'>驱动</font>解读
关于ARM7的键盘与VFD显示器接口技术
  引 言   仪表行业以及工业生产过程对实时性、处理速度、智能化等方面有了更高的要求,而ARM微处理器具有功耗低、指令吞吐量高、实时中断响应、处理器宏单元性价比高等特点,因此,将ARM微处理器引入产品和工业生产的开发设计中已经成为一种必然趋势。   在工业控制和产品设计中,一般都要求具有供操作的键盘和可视化界面。传统显示模块的设计一般都是采用I/O口外扩驱动电路连接而成,显示器一般都采用LED或LCD,要么成本低但实现起来比较复杂,要么实现起来简单成本却很高。笔者综合考虑了成本、实现的难易程度、功能以及稳定性等方面的要求,提出了一种实现起来较容易且具有较高对比度和精度的VFD显示的方法。   1 硬件电路的实现   考虑到
[单片机]
关于<font color='red'>ARM</font>7的键盘与VFD显示器接口技术
linux串口终端驱动——s3c6410平台(二)
1、终端设备 在Linux系统中,终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。tty是Teletype的缩写,Teletype是最早出现的一种终端设备,很像电传打字机,是由Teletype公司生产的。 Linux中包含如下几类终端设备: 1).串行端口终端(/dev/ttySn) 使用计算机串行端口连接的终端设备。串行端口对应的设备名称是/dev/ttySn。如/dev/ttyS0、/dev/ttyS1等,设备号分别是(4,0)、(4,1) 2).伪终端(/dev/pty/) 显示器通常称为控制台终端,若当前进程有控制终端,则/dev/tty就是当前进程的控制终端的设备特殊文件。可
[单片机]
<font color='red'>linux</font>串口终端<font color='red'>驱动</font>——s3c6410平台(二)
建立在ARM基础上的ADSL2+测试仪的设计
引言 近几年来,不对称数字用户线(ADSL)作为网络“最后一公里”问题的理想解决方案在世界范围内得到了广泛运用。在我,ADSL业务已成为运营商收入的主要增点之一。但由于现有电话线路专门为传送低频语音信号所设计,不利于高频信号传输,线路周边环境恶劣且复杂多变,无论ADSL业务的开通还是正常的运营维护,都需进行一系列复杂的测试工作。 现有测试仪表多为国外品牌且价格昂贵,国内产品普遍存在测试频段有限,测试结果片面的问题;而且由于核心芯片处理能力有限,嵌入式操作系统不先进,软件包功能不够完善,对测试结果的管理也比较混乱。随着市场不断壮大,尤其是基于ITU G.992.3/ G.992.5标准的ADSL2/ 2+推出后,价格合理、功能完
[单片机]
建立在<font color='red'>ARM</font>基础上的ADSL2+测试仪的设计
英特尔进军 Arm 芯片领域,并追赶台积电提高代工市场份额
2 月 27 日消息,近日在接受 Tom's Hardware 采访时,英特尔代工负责人斯图尔特・潘(Stu Pann)表示将会进军 Arm 芯片,并不断追赶台积电的代工市场份额。 代工愿景 英特尔希望在 2030 年成为全球第二代代工厂,并希望能成为一家有弹性的代工厂,能够缓解地缘政治、战争冲突等各种问题导致的供应链中断问题。 英特尔会重新平衡其半导体业务,计划产业链的 50% 布局放在美洲 / 欧洲、50% 放在亚洲。 加强和 Arm 合作 Arm 首席执行官雷内・哈斯(Rene Hass)通过远程连接的方式出席 IFS 活动,表示世界似乎正在摆脱独占硬件的想法,转而希望为微软或 Faraday 这样的大公司打造最
[半导体设计/制造]
英特尔进军 <font color='red'>Arm</font> 芯片领域,并追赶台积电提高代工市场份额
负债也要疯狂收购ARM:孙正义的又一次豪赌
       新浪科技 郑峻发自美国硅谷   对于孙正义来说,疯狂从来都是一个褒义词。这位自称孙子后人的日本企业家,在商场上的运筹帷幄令人惊叹。在过去的25年时间里,孙正义用一笔笔令人惊叹的交易,从白手起家打造了一个全球性科技帝国。310亿美元收购英国移动芯片巨头ARM,是他疯狂投资史上的又一个大手笔,但毫无疑问也不是最后一笔。   每两三年爆发一次   “我总是有一些大的想法,每两到三年就会爆发一次”,孙正义如是说。虽然已经年近六十,但他身上那种冒险的基因却从未改变。   的确,每隔几年,孙正义总会以一笔天价收购震惊业界。上个月孙正义先后抛售部分阿里股份和出售游戏开发商SuperCell,总计套现近186亿美元,
[手机便携]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved