历史上的今天
返回首页

历史上的今天

今天是:2024年11月09日(星期六)

正在发生

2021年11月09日 | Mini2440 DM9000 驱动分析(三)

2021-11-09 来源:eefocus

现在开始逐步分析dm9000驱动的probe方法


具体过程见代码中添加的注释


/*

 * Search DM9000 board, allocate space and register it

 */

static int __devinit

dm9000_probe(struct platform_device *pdev)

{

struct dm9000_plat_data *pdata = pdev->dev.platform_data;

struct board_info *db; /* Point a board information structure */

struct net_device *ndev;

const unsigned char *mac_src;

int ret = 0;

int iosize;

int i;

u32 id_val;

 

/* Init network device */

/* 为net_device分配内存,同时这里会为board_info留下了一席之地,追踪alloc_etherdev的实现就可以明了 */

ndev = alloc_etherdev(sizeof(struct board_info));

if (!ndev) {

dev_err(&pdev->dev, "could not allocate device.n");

return -ENOMEM;

}

/* 这里使得ndev->dev.parent=&pdev->dev  */

SET_NETDEV_DEV(ndev, &pdev->dev);

 

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

 

/* 上面已经说过,在为ndev申请内存的时候,同时也为board_info也暂留了一席之地,也也就是说申请的

实际内存大小是sizeof(net_device)+sizeof(board_info),下面把db指针指向这块特意申请的内存 */

/* setup board info structure */

db = netdev_priv(ndev);

 

/* 这里说明ndev->dev.parent=db->dev,也就是说board_info中的dev是ndev的父设备 */

db->dev = &pdev->dev;

/* board_info里面或有一个子结构就是net_device,这里也印证了net_device是board_info的一个子设备 */

db->ndev = ndev;

 

/* 初始化自旋锁 */

spin_lock_init(&db->lock);

/* 初始化互斥锁 */

mutex_init(&db->addr_lock);

/* 初始化等待队列 */

INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);

 

/* 从这里开始获取addr,data寄存器的物理地址,还有irq的物理地址 */

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 resourcesn");

ret = -ENOENT;

goto out;

}

 

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 arean");

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 regn");

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 arean");

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 regn");

ret = -EINVAL;

goto out;

}

 

/* fill in parameters for net-dev structure */

ndev->base_addr = (unsigned long)db->io_addr;

ndev->irq = db->irq_res->start;

 

/* 这里通过判断寄存器地址所占的字节数来确定我们使用哪种通信方式

dm9000_set_io的具体实现在下面代码中给出 */

/* 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首先做初始化,

DM9000_NCR寄存器是Network Control Register,将寄存器的第零为设为1,

表明Software reset and auto clear after 10us

dm9000_reset实现在下面给出 */

dm9000_reset(db);

 

/* try multiple times, DM9000 sometimes gets the read wrong */

for (i = 0; i < 8; i++) {

/* 读取VIDL寄存器的低八位数据,保存在id_val的7-0这八位 */

id_val  = ior(db, DM9000_VIDL);

/* 读取VIDL寄存器的高八位数据,保存在id_val的15-8这八位 */

id_val |= (u32)ior(db, DM9000_VIDH) << 8;

/* 读取PIDL寄存器的低八位数据,保存在id_val的23-16这八位 */

id_val |= (u32)ior(db, DM9000_PIDL) << 16;

/* 读取PIDL寄存器的高八位数据,保存在id_val的31-24这八位 */

id_val |= (u32)ior(db, DM9000_PIDH) << 24;

/* 说明找到了我们相匹配的网卡设备id=0x90000A46 */

if (id_val == DM9000_ID)

break;

dev_err(db->dev, "read wrong id 0x%08xn", id_val);

}

 

if (id_val != DM9000_ID) {

dev_err(db->dev, "wrong id: 0x%08xn", id_val);

ret = -ENODEV;

goto out;

}

 

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

/* SHIPR寄存器的低八位保存着DM9000的type信息,这里读取寄存器中的值,获取到type信息 */

id_val = ior(db, DM9000_CHIPR);

dev_dbg(db->dev, "dm9000 revision 0x%02xn", 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 DM9000En", id_val);

db->type = TYPE_DM9000E;

}

 

/* dm9000a/b are capable of hardware checksum offload */

if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {

db->can_csum = 1;

db->rx_csum = 1;

ndev->features |= NETIF_F_IP_CSUM;

}

 

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

 

/* driver system function */

/* 从这里开始对net_device和board_info两个结构,做必要的初始化 */

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;

 

/* 下面的方法首先通过eeprom方式读取mac地址,

如果使用eeprom方式读取失败,这是会吧platform传入的mac地址作为网络的mac地址,

接着进行检查,如果platform没有给定默认的mac地址,

则会通过读取PAR寄存器所指向的连续6个地址,获取到mac的六位的值,

如果还是没有获取到mac地址,则通知用户通过ifconfig设置mac地址 */

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, 6);

}

 

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);

}

 

memcpy(ndev->dev_addr, "x08x90x90x90x90x90", 6);

 

if (!is_valid_ether_addr(ndev->dev_addr))

dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "

"set using ifconfign", ndev->name);

 

/* 这里把net_device设置进入pdev,之后可以get到net_device进行使用,很重要*/

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;

}

dm9000_set_io

/* dm9000_set_io

 *

 * select the specified set of io routines to use with the

 * device

 */

 

static void dm9000_set_io(struct board_info *db, int byte_width)

{

/* use the size of the data resource to work out what IO

* routines we want to use

*/

 

switch (byte_width) {

case 1:

db->dumpblk = dm9000_dumpblk_8bit;

db->outblk  = dm9000_outblk_8bit;

db->inblk   = dm9000_inblk_8bit;

break;

 

 

case 3:

dev_dbg(db->dev, ": 3 byte IO, falling back to 16bitn");

case 2:

db->dumpblk = dm9000_dumpblk_16bit;

db->outblk  = dm9000_outblk_16bit;

db->inblk   = dm9000_inblk_16bit;

break;

 

case 4:

default:

db->dumpblk = dm9000_dumpblk_32bit;

db->outblk  = dm9000_outblk_32bit;

db->inblk   = dm9000_inblk_32bit;

break;

}

}

dm9000_reset


/* DM9000 network board routine ---------------------------- */

 

static void

dm9000_reset(board_info_t * db)

{

dev_dbg(db->dev, "resetting devicen");

 

/* RESET device */

writeb(DM9000_NCR, db->io_addr);

udelay(200);

writeb(NCR_RST, db->io_data);

udelay(200);

}


probe过程完成,接下来逐个分析driver中的其他方法实现


推荐阅读

史海拾趣

Allied Wire & Cable Inc公司的发展小趣事

Allied公司深知客户是公司发展的核心动力。因此,公司始终将客户服务放在首位,为客户提供全方位、个性化的服务支持。无论是产品咨询、技术支持还是售后服务,Allied公司都力求做到最好,赢得了客户的信任和好评。同时,公司还积极与合作伙伴建立长期稳定的合作关系,共同开拓市场,实现互利共赢。

CW Industries公司的发展小趣事

随着全球电子市场的不断扩大,CW Industries也积极拓展其国际业务。公司通过参加各种国际电子展会和交流活动,与全球各地的客户和合作伙伴建立了广泛的联系。这不仅为公司带来了更多的商机,还使得CW Industries能够及时了解国际市场的最新动态和技术趋势。通过这些国际交流活动,CW Industries不断提升其品牌影响力和市场竞争力。

Fagor Electrónica公司的发展小趣事

然而,随着市场竞争的加剧和技术的快速变革,Fagor Electrónica也面临着前所未有的挑战。为了应对这些挑战,公司不断加大研发投入,积极引进先进技术和管理经验。同时,Fagor Electrónica还加强了与供应商和客户的合作,以共同应对市场变化。这些努力使得公司在激烈的市场竞争中保持了领先地位。

Dresden Elektronik公司的发展小趣事

在技术和产品的双重驱动下,Dresden Elektronik公司开始积极拓展市场。他们不仅在国内市场上取得了显著的成绩,还积极开拓国际市场。通过参加国际展会、与跨国企业合作等方式,公司的知名度和影响力逐渐提升,市场份额也不断扩大。

GTM公司的发展小趣事

在Dresden Elektronik公司创立之初,其创始人看中了电子行业中嵌入式系统技术的巨大潜力。他们凭借对技术的深刻理解和敏锐的市场洞察力,开发出了一系列高性能、低成本的嵌入式系统解决方案。这些创新产品迅速在市场上获得了认可,为公司赢得了第一桶金,也为后续的发展奠定了坚实的基础。

CR Magnetics公司的发展小趣事

CR Magnetics公司成立于1986年,自创立之初,公司便致力于提供高质量的传感器、传感器组件等系列产品。凭借创始人的远见卓识和团队的共同努力,CR Magnetics很快在电子行业中崭露头角。公司总部位于美国密苏里州圣路易斯市,拥有先进的生产设施和仓库,为产品的研发和生产提供了坚实的基础。

问答坊 | AI 解惑

可视倒车雷达

ddddddddddddddddddddddddddddddd…

查看全部问答>

dsp晶振选用

请问dps外接晶振用无源的好还是有源的好?57个问题中说:晶体没有电压的问题,可以适应于任何DSP,建议用无源的晶体,是这样吗?…

查看全部问答>

散分!!!!!!!

恭喜发财,我散分,我发财,大家一起发财!!!!!!…

查看全部问答>

如何选择A/D

我最近要做一个温度检测系统,范围0---99,检测分辨率 0.1℃,在检测电路设计上用铂电阻电桥检测(三线),由于第一次做设计不懂如何选择A\\D,请各位朋友帮忙小弟一下,感激不尽!…

查看全部问答>

eVC++ 对web service 的调用可以么??

论坛以前有这个帖子可是没有明确的答复。 如果谁知道这个问题可以麻烦帮忙解答一下么?? 如果不能调用可以说出根据 或者 文章出处么/? 如果可以调用的话,可以介绍一下方法么?? 谢谢!!…

查看全部问答>

时钟抖动测试-Ivan

时钟抖动测试-Ivan…

查看全部问答>

MSP-EXP430FR5739

这款单片机 用IAR编吗?什么软件下载?…

查看全部问答>

STM32F105 USB OTG 硬件连接

我现在要用STM32F105做个东西要求用到USB OTG功能,但是我不太清楚STM32F105的VBUS引脚该如何连接,是否可以直接连到USB接口的VBUS上,请用过的高人指教下…

查看全部问答>