W5500 ucLinux Driver based on stm32f429

A ucLinux Driver of W5500 for stm32f429
ORIGINAL POST
By dq樣
components
Hardware Components
w5500
X 1
stm32f429
X 1
Software Apps and online services
ucLinux
details

uclinux.jpg 201607041420417558 emberify_iot_11 20161115_150721 20161115_150100 20161115_150542 20161115_150004

之前那篇写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()这个函数:

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;

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

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

数据接收的方式是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:中断名称可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需要参考的数据地址

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()为网卡数据发送函数

设置w5500网卡ip地址为192.168.1.88,用电脑主机去ping,可以ping通,若在linux内核中加了tcp/ip协议栈的话,则可以与电脑主机服务器进行tcp和udp连接,但是这样却没有使用w5500原本自带的硬件tcp/ip协议栈,而是直接绕过去了,我们注意到的是w5500的网络设备驱动程序是设置为原始套接字的形式,所以以后要关注的原始套接字在网络协议中的传输!!
————————————————
版权声明:本文为CSDN博主「dq樣」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jccg89104/article/details/78652474

uclinux.jpg 201607041420417558 emberify_iot_11 20161115_150721 20161115_150100 20161115_150542 20161115_150004

之前那篇写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()这个函数:

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;

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

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

数据接收的方式是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:中断名称可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需要参考的数据地址

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()为网卡数据发送函数

设置w5500网卡ip地址为192.168.1.88,用电脑主机去ping,可以ping通,若在linux内核中加了tcp/ip协议栈的话,则可以与电脑主机服务器进行tcp和udp连接,但是这样却没有使用w5500原本自带的硬件tcp/ip协议栈,而是直接绕过去了,我们注意到的是w5500的网络设备驱动程序是设置为原始套接字的形式,所以以后要关注的原始套接字在网络协议中的传输!!
————————————————
版权声明:本文为CSDN博主「dq樣」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jccg89104/article/details/78652474

COMMENTS

Please Login to comment
  Subscribe  
Notify of
POSTED BY
Reusable S/W