之前那篇写w5500驱动只是单纯的应用程序驱动,虽然可以实现一定的目的,但是没有充分利用到linux的内核,在一些应用场合就显得不合时宜,于是就进行w5500网络设备内核驱动的学习,幸运的是w5500网络设备驱动的文件是在4.8版本的linux内核中找到,但是与我现在使用的2.6.33版本的内核在有些函数和数据结构等都有一定程度上缺失,为此花了很久的一段时间去修修补补这个驱动C文件,终于修补到了编译无错误的程度了,但也经过一定时间的调试才能达到想要的效果,但是通过这样也学习到linux的网络设备方面的知识
       这里只是对w5500网络设备驱动的一些重要的函数及数据结构做简单的分析与总结
       W5500网络设备驱动主要包括在两个文件:w5500.c和w5500-spi.c
       w5100-spi.c构建spi设备驱动以使用spi接口方式来设置w5500,设置w5500的基本读写函数,w5100.c主要构建linux内核的网络设备驱动,设置网络发送接收skb等函数,注意两者紧密相连,缺一不可!修改内核文件中的Kconfig和makefile文件编译这两个C文件。
基本流程为:


在w5100-spi.c主要是用spi的方式来设置w5500的读、写函数,通过其中的w5100_spi_probe函数最后转入到w5100.c中的w5100_probe函数,下面重点关注w5100_spi_probe()这个函数:

int w5100_probe(struct device *dev, const struct w5100_ops *ops,int sizeof_ops_priv, const void *mac_addr, int irq,int link_gpio)

1、 进入这个函数后,首先分配注册网络设备
ndev = alloc_etherdev(alloc_size);
err = register_netdev(ndev);

2、 填充网络设备文件操作结构体
ndev->netdev_ops = &w5100_netdev_ops;

static const struct net_device_ops w5100_netdev_ops = {
.ndo_open = w5100_open,
.ndo_stop = w5100_stop,
.ndo_start_xmit = w5100_start_tx,
.ndo_tx_timeout = w5100_tx_timeout,
.ndo_set_rx_mode = w5100_set_rx_mode,
.ndo_set_mac_address = w5100_set_macaddr,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
};
填充网络设备文件操作,open、stop、.ndo_start_xmit等函数,可见网络设备数据发送函数为w5100_start_tx

3、 填充ethtool_ops结构体
ndev->ethtool_ops = &w5100_ethtool_ops;

  static const struct ethtool_ops w5100_ethtool_ops = {
.get_drvinfo        = w5100_get_drvinfo,
.get_msglevel       = w5100_get_msglevel,
.set_msglevel       = w5100_set_msglevel,
.get_link       = w5100_get_link,
.get_regs_len       = w5100_get_regs_len,
.get_regs       = w5100_get_regs,

};
       ethtool_ops成员函数与用户空间ethool工具的各个命令选项对应,ethtool提供了网卡及网卡驱动管理能力,能够为linux网络开发人员和管理人员提供对网卡硬件、驱动程序和网络协议栈的设置、查看以及调试等功能。

4、 netif_napi_add(ndev, &priv->napi, w5100_napi_poll, 16);
       该函数用于初始化一个NAPI,netif_napi_add()的poll参数是NAPI要调度执行的轮询函数
数据接收流程:


如果是NAPI兼容的设备驱动,则通过poll方式接收数据包,在这种情况下,我们需要为该设备驱动提供作为netif_napi_add()参数的w5100_napi_poll函数:

static int w5100_napi_poll(struct napi_struct *napi, int budget)
{struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);int rx_count;for (rx_count = 0; rx_count < budget; rx_count++) {struct sk_buff *skb = w5100_rx_skb(priv->ndev); //取得接收skb的数据if (skb)netif_receive_skb(skb);//分析网络接收缓冲区的数据elsebreak;}if (rx_count < budget) {napi_complete(napi);//将napi设备从轮询列表中删除w5100_enable_intr(priv); //使能中断}return rx_count;
}

数据接收的方式是NAPI(New API),简单来说,NAPI是综合中断方式与轮询方式的技术
其数据接收流程为:
接收中断来临——》关闭接收中断——》以轮询方式接收所有数据包直到收空——》开启接收中断——》接收中断来临

注册中断函数:
err = request_irq(priv->irq, w5100_interrupt,IRQF_TRIGGER_LOW, ndev->name, ndev);
       priv->irq:要申请的硬件中断号
       w5100_interrupt:向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,ndev参数将被传递给它
       IRQF_TRIGGER_LOW:指定中断触发类型:低电平有效
       ndev->name:设备驱动程序的名称
       ndev:中断名称可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需要参考的数据地址

中断函数:

static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
{
struct net_device *ndev = ndev_instance;
struct w5100_priv *priv = netdev_priv(ndev);int ir = w5100_read(priv, W5100_S0_IR(priv));
if (!ir)
return IRQ_NONE;
w5100_write(priv, W5100_S0_IR(priv), ir);if (ir & S0_IR_SENDOK) {
//      netif_dbg(priv, tx_done, ndev, "tx done\n");
netif_wake_queue(ndev);
}if (ir & S0_IR_RECV) {
w5100_disable_intr(priv);if (priv->ops->may_sleep)
queue_work(priv->xfer_wq, &priv->rx_work); //调度执行一个指定workqueue中的任务。输入参数:
@ workqueue_struct:指定的workqueue指针
@work_struct:具体任务对象指针else if (napi_schedule_prep(&priv->napi)) //检查是否可以进行napi调度
__napi_schedule(&priv->napi);// 在网卡驱动的中断处理函数中调用napi_schedule()来使用NAPI
}return IRQ_HANDLED;
}

5、 创建网卡工作队列
       priv->xfer_wq = alloc_workqueue(netdev_name(ndev), WQ_MEM_RECLAIM, 0);

添加四个任务到工作队列
       INIT_WORK(&priv->rx_work, w5100_rx_work);
       INIT_WORK(&priv->tx_work, w5100_tx_work);
       INIT_WORK(&priv->setrx_work, w5100_setrx_work);
       INIT_WORK(&priv->restart_work, w5100_restart_work);

INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是_func。_func这个任务会需要一些数据作为参数,这个参数就是通过_data传递的。
⑴w5100_rx_work()函数:
w5100_rx_skb():
①skb = netdev_alloc_skb_ip_align(ndev, rx_len);
       netdev_alloc_skb_ip_align会申请一个sk_buff结构,同时申请存放报文数据的buffer空间,并将他们关联起来
②skb_put(skb, rx_len);
       skb_put()修改指向数据区末尾的指针tail,使之往下移len字节,即使数据区向下扩大len字节,并更新数据区长度len,通常在设备驱动的接收数据处理中会调用此函数。
③skb->protocol = eth_type_trans(skb, ndev);
       Linux kernel使用eth_type_trans来判断数据帧的类型,及协议类型

w5100_enable_intr(priv):使能中断

⑵w5100_tx_work()函数:w5100_tx_skb,发送写入skb的函数,最后dev_kfree_skb(skb)释放套接字缓冲区。

⑶w5100_setrx_work(struct work_struct *work)
       w5100_hw_start(priv):w5500硬件启动
       u8 mode = S0_MR_MACRAW;//将w5500设置为原始套接字,此处关键!

⑷w5100_restart_work
       w5100_restart:
①netif_stop_queue(ndev):
       调用linux内核提供的netif_stop_queue()函数,停止设备传输包。
②w5100_hw_reset(priv);
        函数内对w5500设置mac地址,进行socket0的初始化
③netif_wake_queue(ndev);
        当忙于发送的数据包被发送完成后,在以TX结束的中断处理中,应该调用netif_wake_queue(ndev)唤醒被阻塞的上层,以启动它继续向网络设备驱动传送数据包。

6、 数据发送函数
w5100_netdev_ops函数中.ndo_start_xmit = w5100_start_tx,所以w5100_start_tx()为网卡数据发送函数

static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
{struct w5100_priv *priv = netdev_priv(ndev);netif_stop_queue(ndev);// 停止设备传输包if (priv->ops->may_sleep) {WARN_ON(priv->tx_skb);priv->tx_skb = skb;queue_work(priv->xfer_wq, &priv->tx_work);//开启数据发送工作任务} else {w5100_tx_skb(ndev, skb);}return NETDEV_TX_OK;
}
static void w5100_tx_skb(struct net_device *ndev, struct sk_buff *skb)
{struct w5100_priv *priv = netdev_priv(ndev);u16 offset;offset = w5100_read16(priv, W5100_S0_TX_WR(priv));//读w5500的socket0发送缓冲区偏移w5100_writebuf(priv, offset, skb->data, skb->len);w5100_write16(priv, W5100_S0_TX_WR(priv), offset + skb->len);//写数据到w5500ndev->stats.tx_bytes += skb->len;ndev->stats.tx_packets++; //记录发包数量dev_kfree_skb(skb);w5100_command(priv, S0_CR_SEND);
}

最后运行的效果:



       设置w5500网卡ip地址为192.168.1.88,用电脑主机去ping,可以ping通,若在linux内核中加了tcp/ip协议栈的话,则可以与电脑主机服务器进行tcp和udp连接,但是这样却没有使用w5500原本自带的硬件tcp/ip协议栈,而是直接绕过去了,我们注意到的是w5500的网络设备驱动程序是设置为原始套接字的形式,所以以后要关注的原始套接字在网络协议中的传输!!

基于stm32f429的uclinux-W5500网络设备内核驱动相关推荐

  1. win驱动移植linux,LCD移植 - 基于Tiny210v2的Linux-3.9.6内核驱动移植_Linux编程_Linux公社-Linux系统门户网站...

    友善的tiny210v2我买的是7寸电容屏,具体型号得再查查,说是S70. 用原本的LINUX内的SMDKV210的LCD驱动能实现LINUX LOGO的输出,但是有一定的偏差. 主要参考: arm9 ...

  2. 树莓派基于Linux内核驱动开发详解

    一.驱动认知 首先理解Linux内核框图 文件系统认知,Linux内核框图 1.什么是驱动 linux内核驱动.软件层面上的驱动 广义上是指:这一段代码操作了硬件去动,所以这一段代码就叫硬件的驱动程序 ...

  3. linux内核下网络驱动流程,基于Linux内核驱动的网络带宽测速方法与流程

    本发明涉及一种测速方法,尤其是一种网络带宽测速方法. 背景技术: :电信运营商为客户提供一定带宽的Internet接入:为了检验带宽是否达标,一般均由客户使用个人电脑在网页上直接测速.但是随着智能网关 ...

  4. 基于ZYNQ的petalinux 2018.3 DMA驱动的移植和内核编译

    vivado硬件设计 DMA设置 第一步,创建项目 petalinux-create --type project --template zynq --name petalinux_Dma john@ ...

  5. 树莓派基于Linux内核驱动开发

    一.驱动认知 1.1 为什么要学习写驱动 树莓派开发简单是因为有厂家提供的wiringPi库,实现超声波,实现继电器操作,做灯的点亮-都非常简单. 但未来做开发时,不一定都是用树莓派,则没有wirin ...

  6. 第三阶段:43-47.树莓派基于Linux内核驱动开发

    目录 一.驱动认知 1.1 为什么要学习写驱动 1.2 文件名与设备号 1.3 open函数打通上层到底层硬件的详细过程 二.基于框架编写驱动代码 2.1 编写上层应用代码 2.2 修改内核驱动框架代 ...

  7. 《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)网购链接

    <Linux设备驱动开发详解:基于最新的Linux 4.0内核> china-pub   天猫     dangdang   京东 China-pub 8月新书销售榜 推荐序一 技术日新月 ...

  8. 基于μC/OS—III的CC1120驱动程序设计

    基于μC/OS-III的CC1120驱动程序设计 时间:2014-01-21 来源:电子设计工程 作者:张绍游,张贻雄,石江宏 关键字:CC1120   嵌入式操作系统   STM32F103ZE   ...

  9. 开源项目-基于Intel VT技术的Linux内核调试器

    本开源项目将硬件虚拟化技术应用在内核调试器上,使内核调试器成为VMM,将操作系统置于虚拟机中运行,即操作系统成为GuestOS,以这样的一种形式进行调试,最主要的好处就是调试器对操作系统完全透明.如下 ...

最新文章

  1. 【通知】2020年有三AI-CV夏季划升级倒计时,最后两天
  2. STM32开发 -- 低功耗模式详解(1)
  3. yum安装docker(阿里镜像源)及docker-compose二进制安装
  4. python爬虫 爬取有道翻译详解
  5. 本地构建和自动化构建_构建自动化面板
  6. CLR的程序集定位算法(转)
  7. 【Vue2.0】— TodoList案例(十七)
  8. 【转载】解决在Vim中鼠标右键不能粘贴
  9. 奇异值分解与低秩矩阵近似
  10. HDFS某个节点的磁盘满了
  11. Spring中Bean的作用域差别
  12. 信息安全法律法规知识点汇总(郑大信安个人总结版)
  13. 计算机类参考文献 期刊,期刊参考文献标准格式
  14. 考研408数据结构代码
  15. element-tree 实现部门-人员选择(支持ID相同)
  16. 山东理工大学ACM平台题答案 1134 数列求和
  17. ACM 6174问题C++解决
  18. DLNA Samba wifi等名词的初步了解
  19. word分节符,分页符的区别,链接到上一页
  20. 小米9刷鸿蒙,小米手机怎么刷机 小米9刷第三方ROM方法【详解】

热门文章

  1. ARM的嵌入式Linux移植体验之基本概念
  2. 海信机顶盒交换机常用哪些LAN变压器
  3. Linux下查看日志用到的常用命令
  4. ElasticSearch-美丽优雅
  5. 百度为什么不收录、怎么解决?
  6. idea中的编码设置
  7. 金蝶EAS BOS开发之扩展表应用
  8. 什么是 SYN 攻击?如何避免 SYN 攻击?
  9. manjaro 安装mysql_Manjaro安装Mysql
  10. I2C电路:一种超级实用的3.3V/5V双向电平转换电路