一、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-gpiosreset_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_POLLdb->flags |= DM9000_PLATF_SIMPLE_PHY;
#endifdm9000_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);
#endifstatic 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数据结构并传递给上层,各层剥去相应的协议头直至交给用户。


在学习中进步,如有错误,请多多批评指正

linux网络设备驱动之dm9000驱动源码框架解析相关推荐

  1. 【Camera专题】Camera驱动源码全解析_下

    系列文章 1.手把手撸一份驱动 到 点亮 Camera 2.Camera dtsi 完全解析 3.Camera驱动源码全解析上 4.Camera驱动源码全解析下 上篇文章分析了C文件函数的实现,本文继 ...

  2. 基于改进First_order的表情驱动图片系统(源码&教程)

    1.研究背景 早期的Facerig软件可以即时抓取摄像头视频中的人脸关键点,将表情.头部姿态等信息转化到三维虚拟化身上,比如一只猫.一个虚拟卡通人物上.然后在使用即时视频通讯软件时,选择facerig ...

  3. Linux Zero-copy零拷贝技术:源码示例

    <Linux Zero-copy零拷贝技术:源码示例> <Linux Zero-copy零拷贝技术全面揭秘> <什么是mmap?零拷贝?DMA?> <Linu ...

  4. 详解linux下auto工具制作Makefile源码包(制作篇)

    2019独角兽企业重金招聘Python工程师标准>>> 详解linux下auto工具制作Makefile源码包(制作篇) 水木杨 一.     概述 为了更好的制作configure ...

  5. linux epoll 开发指南-【ffrpc源码解析】

    linux epoll 开发指南-[ffrpc源码解析] 摘要 关于epoll的问题很早就像写文章讲讲自己的看法,但是由于ffrpc一直没有完工,所以也就拖下来了.Epoll主要在服务器编程中使用,本 ...

  6. linux下 如何调试php,linux下使用gdb对php源码调试

    title: linux下使用gdb对php源码调试 date: 2018-02-11 17:59:08 tags: --- linux下使用gdb进行php调试 调试了一些php的漏洞,记录一下大概 ...

  7. Linux内存管理 brk(),mmap()系统调用源码分析2:brk()的内存释放流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存释放流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  8. Linux环境下安装MySQL(源码安装)

    Linux环境下安装MySQL(源码安装) 1.事先从官网/国内镜像站点中下载源码安装包,上传至服务器: 2.安装开发工具和开发包(从5.5开始使用cmake编译) 3.创建用户和组 4.编译安装My ...

  9. 图文深度解析Linux内存碎片整理实现机制以及源码

    图文深度解析Linux内存碎片整理实现机制以及源码. 物理内存是以页为单位进行管理的,每个内存页大小默认是4K(大页除外).申请物理内存时,一般都是按顺序分配的,但释放内存的行为是随机的.随着系统运行 ...

  10. Linux中C语言标准库glibc源码下载

    在这篇文章理清gcc.libc.glibc.libc++.libstdc++的关系,我们大概理解了libc,glibc之间的一些关系. 下面我们就开了解一些Linux中C语言标准库glibc源码. 在 ...

最新文章

  1. 批量下载文献中的参考文献
  2. Python将彩色图转换为灰度图
  3. 姚班学霸蝉联第一,清华再霸榜,湘潭大学表现亮眼,第四届 CCF CCSP落下帷幕...
  4. IIS启动时出现0x8ffe2740的错误
  5. html中autocomplete无效,OnChange和AutoComplete都不能使用HTML.TextBox
  6. java定义一个盒子类box_定义一个Box(盒子)类,在该类定义中包括数据成员: length(长),width(宽)和height(...
  7. phpcms调用栏目描述_phpcms v9栏目列表调用每一篇文章内容方法
  8. ApplePay集成教程
  9. django使用mysql事务处理_Django中MySQL事务的使用
  10. BUAA_OO_Unit1总结
  11. 在线等:“实习拿到两个不太好的 offer,去腾讯还是去阿里?”
  12. 程序性能之显示大表格
  13. 麒麟linux怎么安装软件,中标麒麟Linux操作系统怎么安装软件?
  14. 24h的编程比赛总结
  15. 基于MDK1808-EK_T70开发板的miniGUI应用程序演示03: ads1110热电偶温度传感器
  16. word中的方括号怎么删_word中怎么快速删除所有的括号及内容
  17. ajax $.get怎么使用,jquery之ajax之$.get方法的使用
  18. 闭合曲线 网格坐标 matlab,MATLAB曲线绘制
  19. “五子登科”新解之“票子,房子,车子,妻子,孩子”
  20. c语言课设雷霆战机编程,C语言写的雷霆战机

热门文章

  1. STAMP软件 输入文件准备
  2. 新版Edge浏览器88版本系列不支持adobe flash的处理方法
  3. 机器学习的四种学习方法
  4. Android中调用文件管理器进行选择文件(记录)
  5. 关于微信卡券网页跳转链接能力的下线
  6. 触宝输入法+android,触宝输入法国际版
  7. python将pascal VOC数据集转成coco数据集
  8. 天正建筑2016破解版 64位/32位最新版
  9. gradle教程java_gradle入门到精通视频教程 下载
  10. 《Python语言程序设计基础》嵩天著-第4章程序部分练习题答案