linux网络设备驱动之dm9000驱动源码框架解析

一、linux网络设备驱动结构
1.linux网络设备驱动四个层次,
1)网络协议接口层:向网络层协议提供统一的数据包收发接口,不论上层协议是ARP,还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在使得上层协议独立于具体的设备。
2)网络设备接口层:向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。实际上,网络设备接口层从宏观上规划了具体操作硬件的设备驱动功能层的结构。
3)提供实际功能的设备驱动功能层:是网络设备接口层net _device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接收操作。
4)网络设备与媒介层:是完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数在物理上驱动。对于Linux系统而言,网络设备和媒介都可以是虚拟的。
在这里插入图片描述
二、分析dm9000驱动源码
1.先看驱动框架,

/*
 * 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;
	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;
	//得到设备树中regulator配置
	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
	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,  //申请gpio,devm方式申请的自动free
					    "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); //reset脚 置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;
	//安装ndev结构体
	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);
	//从platform获取资源,通过获取的资源找出映射的虚拟寄存器地址
	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;
	}
	//从platform获取中断,并申请和使能中断
	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;
	}
	//ioremap子系统,映射寄存器
	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 */
	//初始化net_device结构体
	ndev->netdev_ops	= &dm9000_netdev_ops;
	ndev->watchdog_timeo	= msecs_to_jiffies(watchdog);
	ndev->ethtool_ops	= &dm9000_ethtool_ops;
	//初始化用户自定义结构体board_info 
	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绑定
	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;
}

#ifdef CONFIG_OF
//匹配设备树compatible的ID表
static const struct of_device_id dm9000_of_matches[] = {
	{ .compatible = "davicom,dm9000", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dm9000_of_matches);
#endif

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

module_platform_driver(dm9000_driver);

MODULE_AUTHOR("Sascha Hauer, Ben Dooks");
MODULE_DESCRIPTION("Davicom DM9000 network driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dm9000");

2.实现网络设备相关结构体net_device_ops 和函数

static const struct net_device_ops dm9000_netdev_ops = {
	//打开网络设备,获取设备需要的IO地址,IRQ,DMA通道等
	.ndo_open		= dm9000_open,
	//停止网络设备,与open相反
	.ndo_stop		= dm9000_stop,
	//启动数据包的发送,关键是sk_buff结构体指针,
	.ndo_start_xmit		= dm9000_start_xmit,
	//数据包发送超时被调用
	.ndo_tx_timeout		= dm9000_timeout,
	//
	.ndo_set_rx_mode	= dm9000_hash_table,
	//网络设备的IO读写
	.ndo_do_ioctl		= dm9000_ioctl,
	//更改最大传输单元
	.ndo_change_mtu		= eth_change_mtu,
	//设置网络设备的特性
	.ndo_set_features	= dm9000_set_features,
	//设置网络设备的特性
	.ndo_validate_addr	= eth_validate_addr,
	//设置设备的MAC地址
	.ndo_set_mac_address	= eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
	//检查网卡收发情况以及是否出错,进行相应处理
	.ndo_poll_controller	= dm9000_poll_controller,
#endif
};

这基本就是dm9000的驱动框架,具体就是填充结构体,去实现驱动的注册到释放,对数据的读写,接下来看下sk_buff结构体。参考地址
3.sk_buff结构体
sk_buff结构体,定义于include/linux/skbuff.h,含义是“套接字缓冲区”,用于linux网络子系统各层之间传递数据,是linux网络子系统数据结构的"中枢",当发送数据包时,linux内核的网络处理模块必须建立一个包含要传输的数据包的sk_buffr,然后将sk_ buff递交给下层,各层在sk_buf中添加不同的协议头直至交给网络设备发送。同样地,当网络设备从网络媒介上接收到数据包后,它必须将接收到的数据转换为sk_buff数据结构并传递给上层,各层剥去相应的协议头直至交给用户。
在这里插入图片描述

在这里插入图片描述
在学习中进步,如有错误,请多多批评指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeAmmon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值