DM9000网卡驱动源码分析系列03 - probe && remove
2015-12-08 08:56
531 查看
static struct dm9000_plat_data *dm9000_parse_dt(struct device *dev) { struct dm9000_plat_data *pdata; struct device_node *np = dev->of_node; const void *mac_addr; if (!IS_ENABLED(CONFIG_OF) || !np) return ERR_PTR(-ENXIO); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); if (of_find_property(np, "davicom,ext-phy", NULL)) pdata->flags |= DM9000_PLATF_EXT_PHY; if (of_find_property(np, "davicom,no-eeprom", NULL)) pdata->flags |= DM9000_PLATF_NO_EEPROM; mac_addr = of_get_mac_address(np); if (mac_addr) memcpy(pdata->dev_addr, mac_addr, sizeof(pdata->dev_addr)); return pdata; } /* * 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); 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; 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); 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 { /* 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; ndev->irq = db->irq_res->start; /* 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)) { dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " "set using ifconfig\n", ndev->name); eth_hw_addr_random(ndev); mac_src = "random"; } 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; } static int dm9000_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); unregister_netdev(ndev); dm9000_release_board(pdev, netdev_priv(ndev)); free_netdev(ndev); /* free device structure */ dev_dbg(&pdev->dev, "released and freed device\n"); return 0; }
通过module_platform_driver(dm9000_driver);把驱动放到platform总线上
如果有dm9000设备出现,就会调用probe函数,反之,则调用remove函数,下面我们来看看probe函数的实现
static struct dm9000_plat_data *dm9000_parse_dt(struct device *dev) { struct dm9000_plat_data *pdata; struct device_node *np = dev->of_node; const void *mac_addr; if (!IS_ENABLED(CONFIG_OF) || !np) return ERR_PTR(-ENXIO); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); if (of_find_property(np, "davicom,ext-phy", NULL)) pdata->flags |= DM9000_PLATF_EXT_PHY; if (of_find_property(np, "davicom,no-eeprom", NULL)) pdata->flags |= DM9000_PLATF_NO_EEPROM; mac_addr = of_get_mac_address(np); if (mac_addr) memcpy(pdata->dev_addr, mac_addr, sizeof(pdata->dev_addr)); return pdata; } /* * 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); 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; if (!pdata) { pdata = dm9000_parse_dt(&pdev->dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); }通过通用的途径来获取设备数据,因为匹配操作是platform执行的,那么总线告诉驱动有匹配的设备的时候也会提供设备的信息
那么信息就是通过dev_get_platdata函数来获取的,如果获取不到这个信息就自己通过设备树获取,
从dm9000_parse_dt函数来看,并没有获取很多的信息,只是获取flags,还有网卡的mac地址
那些以of开头的函数是设备树提供的API
/* 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);首先分配空间alloc_etherdev函数是alloc_netdev的封装,参数就是netdev_priv返回的指针指向的大小
SET_NETDEV_DEV宏的定义如下
#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))
通过netdev_priv获取私有数据后, 初始化一些值,自旋锁,及互斥锁
INIT_DELAYED_WORK宏的作用是设置
db->phy_poll.work.func = dm9000_poll_work;
这个delay_work的作用就是延迟执行,通过调用schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
第二个参数delay的解释是:@delay: number of jiffies to wait or 0 for immediate execution, 既多少个jiffies后执行func,如果是0就立即执行
static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct board_info *db = netdev_priv(dev); unsigned long flags; unsigned nsr, wcr; spin_lock_irqsave(&db->lock, flags); nsr = ior(db, DM9000_NSR); wcr = ior(db, DM9000_WCR); dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr); if (nsr & NSR_WAKEST) { /* clear, so we can avoid */ iow(db, DM9000_NSR, NSR_WAKEST); if (wcr & WCR_LINKST) dev_info(db->dev, "wake by link status change\n"); if (wcr & WCR_SAMPLEST) dev_info(db->dev, "wake by sample packet\n"); if (wcr & WCR_MAGICST) dev_info(db->dev, "wake by magic packet\n"); if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST))) dev_err(db->dev, "wake signalled with no reason? " "NSR=0x%02x, WSR=0x%02x\n", nsr, wcr); } spin_unlock_irqrestore(&db->lock, flags); return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE; } /** 中断处理函数,看代码就是打印出被唤醒的原因,因为网卡状态改变,包来了,或是信号等原因被唤醒 */ 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 { /* 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; ndev->irq = db->irq_res->start;这一块代码初始化设备的IO内存,还有中断号,首先通过platform_get_resource函数获取到resource资源
wake那一块代码不是很清楚什么作用,
在linux kernel中,调用enable_irq_wake函数,可以将一个irq具有唤醒系统的功能,即把系统从低功耗模式中唤醒
static inline int enable_irq_wake(unsigned int irq)
{
return irq_set_irq_wake(irq, 1);
}
Linux内核提供了一组函数以申请和释放IO内存的范围,这里的申请表明该驱动要访问这片区域,它不会做任何内存映射的动作
通过调用request_mem_region函数来申请,通过调用release_mem_region来释放,将IO内存返回给系统
在内核中访问IO内存之前,需首先使用ioremap函数将设备所处的物理地址映射到虚拟地址上,不再使用IO后,通过iounmap释放
/* 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; }IO内存已经申请且映射好了,我们需要封装一些函数来实现对IO内存的读写操作
in, out是从驱动的角度看数据的流入,流出,in是指从网卡到驱动,out是指从驱动到网卡,dump就是把垃圾数据dump掉
要根据网卡的特点注册不同的函数,因为如果网卡支持一次4字节读写,那么就不应该一次读写1个或2个字节,以提高效率
#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; }获取设备的网卡类型,同样是dm9000设备,也有dm9000a, dm9000b, dm9000e, 不同的型号之间有些功能的支持也不同
比如dm9000a, dm9000b支持校验和操作,减轻上层负担
static void dm9000_reset(struct board_info *db) { dev_dbg(db->dev, "resetting device\n"); /* Reset DM9000, see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 * The essential point is that we have to do a double reset, and the * instruction is to set LBK into MAC internal loopback mode. */ iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK); udelay(100); /* Application note says at least 20 us */ if (ior(db, DM9000_NCR) & 1) dev_err(db->dev, "dm9000 did not respond to first reset\n"); iow(db, DM9000_NCR, 0); iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK); udelay(100); if (ior(db, DM9000_NCR) & 1) dev_err(db->dev, "dm9000 did not respond to second reset\n"); }网卡的重置,都是对寄存器的操作,可以参考DM9000_Application_Notes_Ver_1_22.pdf
下载地址:http://download.csdn.net/detail/shuimuyq/9336125
/* 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)) { dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " "set using ifconfig\n", ndev->name); eth_hw_addr_random(ndev); mac_src = "random"; } 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; }设置/注册netdev_ops ethtool_ops, netdev_ops是设备支持的操作的实现, ethtool_ops是ethtool工具支持的操作的实现
watchdog_timeo是发送超时设置
mii(Medium Independent Interface (10M/100M)) 网卡设备对ioctl的支持,很多都是已经实现了,不需要重写
接下来是获取网卡地址,首先读取eeprom(Electrically Erasable Programmable Read-Only Memory电可擦可编程只读存储器)
如果读不到则去platform总线获取网卡地址,再读不到就去设备IO内存读取网卡地址,再读不到就随机生成网卡地址并警告
static unsigned char dm9000_type_to_char(enum dm9000_type type) { switch (type) { case TYPE_DM9000E: return 'e'; case TYPE_DM9000A: return 'a'; case TYPE_DM9000B: return 'b'; } return '?'; } /* dm9000_release_board * * release a board, and any mapped resources */ static void dm9000_release_board(struct platform_device *pdev, struct board_info *db) { /* unmap our resources */ iounmap(db->io_addr); iounmap(db->io_data); /* release the resources */ if (db->data_req) release_resource(db->data_req); kfree(db->data_req); if (db->addr_req) release_resource(db->addr_req); kfree(db->addr_req); } static int dm9000_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); unregister_netdev(ndev); dm9000_release_board(pdev, netdev_priv(ndev)); free_netdev(ndev); /* free device structure */ dev_dbg(&pdev->dev, "released and freed device\n"); return 0; }这几个函数都很短,很好理解,dm9000_type_to_char根据设备类型,返回对应字母,id_val --> db->type
dm9000_release_board函数是在probe出错,或者remove时被调用,被映射的资源都应该释放
有一点不理解的是:db->data_req,db->addr_req都是通过request_mem_region申请的,为什么不是release_mem_region释放
dm9000_drv_remove函数在设备被移除时调用,就是probe的反操作,顺序要注意,在probe时register_netdev是在最后被调用
调用后就以为着内核可以使用这个设备了,在remove时unregister_netdev要在最开始被调用
相关文章推荐
- 17.1 简单文本输出
- Linux之旅——Linux简介
- MySQL复合分区
- 详解Java的Spring框架下bean的自动装载方式
- http://www.cnblogs.com/xwdreamer/archive/2012/02/21/2360818.html
- 保留四位有效小数
- 手机充电(练习)
- SAP 差旅报销集成方案的实现
- iOS UILabel利用NSMutableString显示不同样式的文字(富文本)
- ASP LyfUpload组件的用法
- Struts2学习——(八)Struts2标签II
- 解决安卓SDK更新连不通问题
- poj 2739(筛法求素数)
- IBM X服务器BIOS 使用说明
- [转]java.lang.instrument 学习(一)
- 解决应用崩溃后,提示应用异常问题,只是取消提示。
- 软件开发环境的配置
- 生活耳语
- Windows 环境下配置MXNET教程
- java中Set使用介绍