2025年2月25日 星期二 甲辰(龙)年 腊月廿五 设为首页 加入收藏
rss
您当前的位置:首页 > 电子 > 无线/电,信号与通信

USB网卡收发数据分析

时间:11-22来源:作者:点击数:39
CDSY,CDSY.XYZ

前言

网络子系统是 Linux 内核的重中之重。今天,我们从网卡驱动入手,开始网络子系统的探索之路。

硬件环境

请添加图片描述

树莓派3B+,搭载千兆网卡 LAN7515,不过其使用的是 USB2.0 通道(最高理论速度为480Mbps),官方给出 300Mbps 的以太网传输速率。

驱动

LAN7515 的驱动为 lan78xx,源码比较简单,就两个文件 ./linux/drivers/net/usb/lan78xx.c/h,编译后产生 lan78xx.ko 驱动文件。

驱动分析

1. USB 驱动注册
  • static struct usb_driver lan78xx_driver = {
  • .name = DRIVER_NAME,
  • .id_table = products,
  • .probe = lan78xx_probe,
  • .disconnect = lan78xx_disconnect,
  • .suspend = lan78xx_suspend,
  • .resume = lan78xx_resume,
  • .reset_resume = lan78xx_reset_resume,
  • .supports_autosuspend = 1,
  • .disable_hub_initiated_lpm = 1,
  • };
  • module_usb_driver(lan78xx_driver);

LAN7515 是 USB 网卡,和 CPU 交互的接口为 USB,所以它是一个 USB 设备,要在内核注册它的 USB 驱动——lan78xx.ko。

insmod lan78xx.ko 即在 USB 总线上注册 LAN7515 网卡驱动,此时,会遍历 USB 总线上已注册的设备,用总线的 match 方法判断是否有匹配的设备,如果有,则调用驱动的 probe 函数。

具体看一下:

在这里插入图片描述

/sys/bus/usb/ 下有 devices 和 drivers 目录,这两个目录下分别是当前系统中 USB 总线上已注册的设备已注册的驱动

可以看到,在 drivers 目录中目前还没有 lan78xx 驱动,并且我们也不知道 devices 目录中的哪个 USB 设备为 LAN7515 设备。

下面我们安装 lan78xx 驱动

在这里插入图片描述

看到执行了 lan78xx_probe() 函数,这也印证了我们上面的说法(注册驱动,会遍历设备,寻求匹配,匹配成功,触发 probe)。

我们再次查看 /sys/bus/usb/drivers 下的目录情况

在这里插入图片描述

可以看到,已经产生了 lan78xx 目录,说明驱动注册成功了。

2. 驱动和设备绑定

在 lan78xx_probe() 函数中调用了 lan78xx_bind() 方法,正式地将 lan78xx 驱动和 1-1.1.1:1.1 设备绑定了起来。在 lan78xx 目录下也能体现这一点。

在这里插入图片描述

我们也可以手动绑定和解绑

  • # ifconfig -a
  • lo Link encap:Local Loopback
  • inet addr:127.0.0.1 Mask:255.0.0.0
  • inet6 addr: ::1/128 Scope:Host
  • UP LOOPBACK RUNNING MTU:65536 Metric:1
  • RX packets:42 errors:0 dropped:0 overruns:0 frame:0
  • TX packets:42 errors:0 dropped:0 overruns:0 carrier:0
  • collisions:0 txqueuelen:1000
  • RX bytes:4478 (4.3 KiB) TX bytes:4478 (4.3 KiB)
  • # ls
  • bind module new_id remove_id uevent unbind
  • # echo "1-1.1.1:1.0" > bind
  • [37493.831496] lan78xx_probe()
  • [37494.126379] lan78xx 1-1.1.1:1.0 (unnamed net_device) (uninitialized): No External EEPROM. Setting MAC Speed
  • [37494.144756] libphy: lan78xx-mdiobus: probed
  • [37494.264385] lan78xx 1-1.1.1:1.0 (unnamed net_device) (uninitialized): int urb period 64
  • [37494.276093] lan78xx_phy_init()
  • # ls
  • 1-1.1.1:1.0 module remove_id unbind
  • bind new_id uevent
  • # ifconfig -a
  • eth0 Link encap:Ethernet HWaddr B8:27:EB:8A:BC:F4
  • BROADCAST MULTICAST MTU:1500 Metric:1
  • RX packets:0 errors:0 dropped:0 overruns:0 frame:0
  • TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
  • collisions:0 txqueuelen:1000
  • RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
  • lo Link encap:Local Loopback
  • inet addr:127.0.0.1 Mask:255.0.0.0
  • inet6 addr: ::1/128 Scope:Host
  • UP LOOPBACK RUNNING MTU:65536 Metric:1
  • RX packets:42 errors:0 dropped:0 overruns:0 frame:0
  • TX packets:42 errors:0 dropped:0 overruns:0 carrier:0
  • collisions:0 txqueuelen:1000
  • RX bytes:4478 (4.3 KiB) TX bytes:4478 (4.3 KiB)
  • #
  • #
  • # echo "1-1.1.1:1.0" > unbind
  • # ls
  • bind module new_id remove_id uevent unbind
  • # ifconfig -a
  • lo Link encap:Local Loopback
  • inet addr:127.0.0.1 Mask:255.0.0.0
  • inet6 addr: ::1/128 Scope:Host
  • UP LOOPBACK RUNNING MTU:65536 Metric:1
  • RX packets:42 errors:0 dropped:0 overruns:0 frame:0
  • TX packets:42 errors:0 dropped:0 overruns:0 carrier:0
  • collisions:0 txqueuelen:1000
  • RX bytes:4478 (4.3 KiB) TX bytes:4478 (4.3 KiB)
  • #
3. 数据收发

以 PC1 数据链路层发送一个数据包给 PC2 数据链路层为例

请添加图片描述

rx

  • tasklet_schedule(&dev->bh); // USB 收到数据时,产生中断,触发该任务
  • lan78xx_bh()
  • lan78xx_rx_bh()
  • rx_submit()
  • usb_fill_bulk_urb() // 从 USB 读取数据
  • tasklet_schedule(&dev->bh);
  • lan78xx_bh()
  • rx_process()
  • lan78xx_rx()
  • lan78xx_skb_return()
  • -----上面为驱动-------下面为数据链路层-----------------------
  • netif_rx() // 送入数据链路层

tx

  • struct net_device_ops {
  • .ndo_start_xmit = lan78xx_start_xmit,
  • }
  • -----上面为数据链路层-------下面为驱动-----------------------
  • lan78xx_start_xmit()
  • skb_queue_tail() // 插入队列
  • tasklet_schedule(&dev->bh); // 触发任务调度 lan78xx_bh
  • lan78xx_bh()
  • lan78xx_tx_bh()
  • skb_dequeue() // 取出数据
  • ------上面为驱动网络部分--------下面为驱动 USB 部分-----------
  • usb_fill_bulk_urb() // 填充数据到 USB
  • usb_submit_urb() // USB 发送数据

RX call tree:目前理得有点混乱,大致过程是 USB 硬件产生中断,驱动去捞数据,经过 rx_process() 处理后,最终调用 netif_rx(skb),将数据送入内核网络子系统,进一步处理 skb。

TX call tree:内核网络子系统最终调用 ndo_start_xmit() 发送数据,该接口最终由设备驱动实现,本示例为 lan78xx_start_xmit(),最终将数据通过 USB 发送给 LAN7515 网卡,网卡再经过自己的硬件加工,最终将数据发送到网线。

在这里插入图片描述

打印帧头

  • static netdev_tx_t
  • lan78xx_start_xmit(struct sk_buff *skb, struct net_device *net)
  • {
  • printk("%s()\n", __FUNCTION__);
  • struct ethhdr *eth = eth_hdr(skb);
  • printk(KERN_INFO "DEST:" MAC_FMT "\n", MAC_ARG(eth->h_dest));
  • printk(KERN_INFO "SOURCE:" MAC_FMT "\n", MAC_ARG(eth->h_source));
  • struct iphdr *ip_header = ip_hdr(skb);
  • printk("daddr:%x is %d.%d.%d.%d\n", ip_header->daddr, inet_ntoa(ip_header->daddr));
  • printk("saddr:%x is %d.%d.%d.%d\n\n", ip_header->saddr, inet_ntoa(ip_header->saddr));
  • ...
  • [44851.676380] lan78xx_start_xmit()
  • [44851.680906] DEST:08:00:27:38:fc:d0
  • [44851.685656] SOURCE:b8:27:eb:8a:bc:f4
  • [44851.690571] daddr:6401a8c0 is 192.168.1.100
  • [44851.696164] saddr:101a8c0 is 192.168.1.1

打印 TX 方向,驱动送给网卡的数据帧中的目的 MAC、源 MAC、目的 IP、源 IP,即上图中 ① 处的数据帧头。

总结

网卡、网卡驱动、内核网络子系统,它们干的事情核心就是传递数据包,其中 sk_buff 在整个过程中起着穿针引线的作用,这次分析网卡驱动收发的过程中,也尝试打印了 sk_buff 的部分信息,作为一个探索的开始吧。其实 sk_buff 在网络子系统中确实起着非常重要的作用,sk_buff 是 socket buffer 的意思,这又进一步体现了 socket 的重要,连其 buffer 都那么 NB,socket 不更 NB 吗?所以后面的研究路线是:driver --> sk_buff --> socket

CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐