DM9000网卡驱动源码分析系列03 - probe && remove

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要在最开始被调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值