USB网卡驱动分析(rt8152)

  • USB设备驱动程序分析
    • USB总线和USB设备
    • USB设备匹配方式
    • 思考
  • 网络设备驱动程序分析
    • 网卡设备驱动分析
    • USB+网卡驱动
    • linux 源码中rt8152驱动程序分析
    • 总结

USB设备驱动程序分析

最近一直在搞zynq的PL部分,为了保持对驱动程序的敏感度,看着源码分析一下rt8152的驱动程序。之前学单片机一直想着给单片机装一个USB网卡,但是一直没有思路。今天突然想到之前的想法,就带着这个想法加上对内核驱动的怀念,写一下,写点东西有时候能让人的心平静点。

USB总线和USB设备

usb总线是一种常见的高速总线,其实也不算高,usb2.0规定没记错应该是480Mbit/s。物理层上,usb high使用的是差分电压型串行数据线。usb full采用的是差分电流型串行串行数据线。差分抗干扰源于模拟电路的一个差动放大器,放大差模信号,抑制共模信号。再来说一下为什么高速数据总线喜欢用电流来传输信号。我们知道当导线距离增加时,其电阻会增加,那么从发送端到接收端的电压就会发生变化。使用电流传输,在发送端放置一个电流源,无论电阻怎么变换,流过电阻的电流总是恒定的。只要在接收端的端接电阻保持稳点就可以了。
简单介绍完物理层以后,在说一下USB通信的过程。很重要的一点是每次USB通信的发起者都是主机。这点很重要,至于USB的中断传输模式、块传输。。。网上好多,这里只写一些实用的、简单的。
在linux设备驱动中,把usb控制器的驱动程序可以看成是usb总线驱动程序,在设备树中定义了usb控制器的设备节点,控制器通过和设备树匹配,获取到usb控制器的基地址、中断号等资源。这部分程序一般不需要驱动工程师去修改,一般芯片厂商会写好。一般情况下,需要关心的是USB设备驱动程序,也就是挂在USB总线上的设备。通俗的讲就是u盘,usb声卡,usb暖手宝(这算吗☺)。

USB设备匹配方式

usb设备的匹配不需要在设备树中进行定义,而是通过vid和pid。这是usb控制器在在完成热插拔以后做的一项检测工作,会从usb的端点0中读取usb的vid和pid,然后在和已经装在的usb驱动程序进行匹配。

static struct usb_device_id rtl8152_table[] = {{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},{REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)},{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7205)},{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x304f)},{REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff)},{}
};

这是usb网卡的设备ID匹配表。当usb设备接到usb总线上,如果正确匹配,probe函数就会被执行。

思考

  1. usb设备驱动的PID和VID能不能动态配置?
  2. usb设备驱动程序中的VID和PID万一重复怎么办?

网络设备驱动程序分析

网卡设备驱动分析

之前写过一篇 linux网卡设备驱动(任意传输介质传输(与FPGA交互)) 大致说了下网卡驱动程序的结构以及怎么实现一个网络驱动程序。
这里在赘述一下,网卡驱动程序在probe函数中:

  1. struct net_device *netdev;定义网络设备结构体;
  2. netdev = alloc_etherdev(sizeof(struct r8152));分配空间
  3. 填充该结构体;
  4. ret = register_netdev(netdev);注册该设备;
  5. 实现传输函数以及中断接收网络数据。(也可以是轮询,取决于并发量,目前有一种自动检测的驱动程序,第一次是中断形式,之后是轮询);

USB+网卡驱动

标题其实已经说明了,USB网卡驱动程序就是USB驱动程序加上网卡驱动程序。说说题外话吧,如何用带usb控制器的单片机来和usb网卡进行数据交互呢?之前做过STM32上的USB自定义设备程序,是从机的(上位机用的是libusb+qt)。所以对单片机的usb程序有一些记忆,基本都是对端点的操作。接下来去分析linux中的usb网卡驱动程序,以此来构想下单片机如何驱动一个usb网卡。

linux 源码中rt8152驱动程序分析

源码在drivers\net\usb\r8152.c下
先看下入口函数:

static int rtl8152_probe(struct usb_interface *intf,const struct usb_device_id *id)
{struct usb_device *udev = interface_to_usbdev(intf);struct r8152 *tp;struct net_device *netdev;int ret;usb_driver_set_configuration(udev, 1);usb_reset_device(udev);netdev = alloc_etherdev(sizeof(struct r8152));tp->mii.dev = netdev;tp->mii.mdio_read = read_mii_word;tp->mii.mdio_write = write_mii_word;tp->mii.phy_id_mask = 0x3f;tp->mii.reg_num_mask = 0x1f;tp->mii.phy_id = R8152_PHY_ID;ret = register_netdev(netdev);
}

把结构性的代码留下了,可以看出在probe函数中,完成了usb操作接口的获取、网络设备的注册、pyh相关设置。

static int rtl_ops_init(struct r8152 *tp)
{struct rtl_ops *ops = &tp->rtl_ops;int ret = 0;switch (tp->version) {case RTL_VER_01:case RTL_VER_02:ops->init      = r8152b_init;ops->enable       = rtl8152_enable;ops->disable       = rtl8152_disable;ops->up           = rtl8152_up;ops->down      = rtl8152_down;ops->unload      = rtl8152_unload;ops->eee_get       = r8152_get_eee;ops->eee_set        = r8152_set_eee;ops->in_nway        = rtl8152_in_nway;ops->hw_phy_cfg       = r8152b_hw_phy_cfg;ops->autosuspend_en = rtl_runtime_suspend_enable;break;case RTL_VER_03:case RTL_VER_04:case RTL_VER_05:case RTL_VER_06:ops->init        = r8153_init;ops->enable        = rtl8153_enable;ops->disable       = rtl8153_disable;ops->up           = rtl8153_up;ops->down      = rtl8153_down;ops->unload      = rtl8153_unload;ops->eee_get       = r8153_get_eee;ops->eee_set        = r8153_set_eee;ops->in_nway        = rtl8153_in_nway;ops->hw_phy_cfg       = r8153_hw_phy_cfg;ops->autosuspend_en  = rtl8153_runtime_enable;break;default:ret = -ENODEV;netif_err(tp, probe, tp->netdev, "Unknown Device\n");break;}return ret;
}

这段代码完成了所有网卡的操作函数的注册。

static const struct net_device_ops rtl8152_netdev_ops = {.ndo_open      = rtl8152_open,.ndo_stop       = rtl8152_close,.ndo_do_ioctl      = rtl8152_ioctl,.ndo_start_xmit        = rtl8152_start_xmit,.ndo_tx_timeout       = rtl8152_tx_timeout,.ndo_set_features = rtl8152_set_features,.ndo_set_rx_mode    = rtl8152_set_rx_mode,.ndo_set_mac_address = rtl8152_set_mac_address,.ndo_change_mtu      = rtl8152_change_mtu,.ndo_validate_addr    = eth_validate_addr,.ndo_features_check    = rtl8152_features_check,
};

rtl8152_start_xmit()函数很重要,完成了网络数据包的发送。实现如下:


static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb,struct net_device *netdev)
{struct r8152 *tp = netdev_priv(netdev);skb_tx_timestamp(skb);skb_queue_tail(&tp->tx_queue, skb);if (!list_empty(&tp->tx_free)) {if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {set_bit(SCHEDULE_NAPI, &tp->flags);schedule_delayed_work(&tp->schedule, 0);} else {usb_mark_last_busy(tp->udev);napi_schedule(&tp->napi);}} else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen) {netif_stop_queue(netdev);}return NETDEV_TX_OK;
}

再到usb中断服务函数中找到接收大代码:

static void tx_bottom(struct r8152 *tp)
{int res;do {struct tx_agg *agg;if (skb_queue_empty(&tp->tx_queue))break;agg = r8152_get_tx_agg(tp);if (!agg)break;res = r8152_tx_agg_fill(tp, agg);if (res) {struct net_device *netdev = tp->netdev;if (res == -ENODEV) {set_bit(RTL8152_UNPLUG, &tp->flags);netif_device_detach(netdev);} else {struct net_device_stats *stats = &netdev->stats;unsigned long flags;netif_warn(tp, tx_err, netdev,"failed tx_urb %d\n", res);stats->tx_dropped += agg->skb_num;spin_lock_irqsave(&tp->tx_lock, flags);list_add_tail(&agg->list, &tp->tx_free);spin_unlock_irqrestore(&tp->tx_lock, flags);}}} while (res == 0);
}

这里使用了中断的下半部,说一下这个知识,在linux内核中,中断处理一般分为以下几种方式:

  1. 直接处理;
  2. 软中断
  3. tasklet;
  4. 中断线程化;
  5. 工作队列;

直接处理就是在中断服务函数中直接处理相应的逻辑,这种情况对应于十分简短的中断处理逻辑;
软中断和tasklet都是发生在中断上下文的,因此在处理函数中不能有休眠。工作队列和中断线程化可以用休眠,但是处理的实时性会差一些。
大体结构已经出来了,还有一个urb的知识没有说;在linux驱动中,通过驱动程序通过urb和设备进行通信。urb相当于网络设备驱动程序中的skb。是传输数据的载体。

static void write_bulk_callback(struct urb *urb)
{struct net_device_stats *stats;struct net_device *netdev;struct tx_agg *agg;struct r8152 *tp;int status = urb->status;agg = urb->context;if (!agg)return;tp = agg->context;if (!tp)return;netdev = tp->netdev;stats = &netdev->stats;if (status) {if (net_ratelimit())netdev_warn(netdev, "Tx status %d\n", status);stats->tx_errors += agg->skb_num;} else {stats->tx_packets += agg->skb_num;stats->tx_bytes += agg->skb_len;}spin_lock(&tp->tx_lock);list_add_tail(&agg->list, &tp->tx_free);spin_unlock(&tp->tx_lock);usb_autopm_put_interface_async(tp->intf);if (!netif_carrier_ok(netdev))return;if (!test_bit(WORK_ENABLE, &tp->flags))return;if (test_bit(RTL8152_UNPLUG, &tp->flags))return;if (!skb_queue_empty(&tp->tx_queue))napi_schedule(&tp->napi);
}static void intr_callback(struct urb *urb)
{struct r8152 *tp;__le16 *d;int status = urb->status;int res;tp = urb->context;if (!tp)return;if (!test_bit(WORK_ENABLE, &tp->flags))return;if (test_bit(RTL8152_UNPLUG, &tp->flags))return;switch (status) {case 0:          /* success */break;case -ECONNRESET:    /* unlink */case -ESHUTDOWN:netif_device_detach(tp->netdev);case -ENOENT:case -EPROTO:netif_info(tp, intr, tp->netdev,"Stop submitting intr, status %d\n", status);return;case -EOVERFLOW:netif_info(tp, intr, tp->netdev, "intr status -EOVERFLOW\n");goto resubmit;/* -EPIPE:  should clear the halt */default:netif_info(tp, intr, tp->netdev, "intr status %d\n", status);goto resubmit;}d = urb->transfer_buffer;if (INTR_LINK & __le16_to_cpu(d[0])) {if (!netif_carrier_ok(tp->netdev)) {set_bit(RTL8152_LINK_CHG, &tp->flags);schedule_delayed_work(&tp->schedule, 0);}} else {if (netif_carrier_ok(tp->netdev)) {set_bit(RTL8152_LINK_CHG, &tp->flags);schedule_delayed_work(&tp->schedule, 0);}}resubmit:res = usb_submit_urb(urb, GFP_ATOMIC);if (res == -ENODEV) {set_bit(RTL8152_UNPLUG, &tp->flags);netif_device_detach(tp->netdev);} else if (res) {netif_err(tp, intr, tp->netdev,"can't resubmit intr, status %d\n", res);}
}

这是在usb网卡驱动中操作urb的实例,看起来很像i2c驱动中的i2c_msg;
usb驱动驱动具体操作下次会具体的说明。这次重点是usb网卡的结构分析;
回到最初的愿望,用单片机去控制usb网卡,实现单片机上网可行吗?
答案是可行的,首先要让单片机上有一个简单的操作系统,这样方便网络协议栈的移植,然后就是把单片机的usb host 调好,通过读取usb设备的端点来实现usb网卡和单片机的数据交互。之后有时间做一下这个,实现下最初的理想。人有时候在不具备条件时,总是幻想着自己的梦想,当具备条件时,最初的梦想早已忘得一干二净。

总结

  1. 在linux中,对usb抽象做的比较好,记得当时调单片机的usb调了一个周才实现了基本的数据交互,linux下只要你插上usb就会识别到总线驱动,前提是控制器正常工作,不然可比单片机难搞。
  2. SDIO网卡有时间研究下,据说安卓好多用的是SDIO的网卡芯片。

USB网卡驱动分析(rt8152)相关推荐

  1. linux 网卡驱动分析,LINUX_网卡驱动分析

    LINUX_网卡驱动分析 (36页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 Linux DM9000网卡驱动程序完全分析说明仁 本文分 ...

  2. linux下usb转串口驱动分析

    linux下usb转串口驱动分析 分类: linux driver 2012-06-08 15:11 456人阅读 评论(0) 收藏 举报 linux struct interface returni ...

  3. linux安装comfast网卡驱动,电脑如何通过usb共享手机网络 Linux安装wifi 无线网络 811AC usb网卡驱动...

    电脑如何通过usb共享手机网络 该方法是通过USB线将手机和电脑连接的方式来共享网络,所以不管是笔记本电脑还是台式机,不管电脑有无线网卡,都可以使用该方法. 准备工作:首先用数据线把手机连接到电脑上, ...

  4. Linux PCI网卡驱动分析

    http://www.uplinux.com/shizi/wenxian/4429.html Linux网卡驱动分析 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难 ...

  5. USB转串口驱动分析(一)

    之前追踪代码用的grep命令效率太低了,所以这次下载C代码阅读跳转利器ctags.cscope用于分析代码 因为用的是Centos6.7所以需要用到yum install安装软件 [wuyujun@w ...

  6. Debian系统源码安装usb网卡驱动

    系统为debian 9.6 64位版本,安装网卡驱动为asix的 AX88772B芯片 1. 安装系统build模块 apt-get install linux-image-$(uname -r) l ...

  7. ubuntu 16.04/18.04安装TP-LINK TL-WDN5200H无线USB网卡驱动

    TP-LINK TL-WDN5200H无线USB网卡驱动没有自带ubuntu系统的驱动 参考如下两篇文章后搞定: 1.https://blog.csdn.net/F_BQ333/article/det ...

  8. ubuntu 16.04安装TP-LINK TL-WDN5200H无线USB网卡驱动

    TP-LINK TL-WDN5200H无线USB网卡驱动没有自带ubuntu系统的驱动 参考如下两篇文章后搞定: 1.https://blog.csdn.net/F_BQ333/article/det ...

  9. arm平台linux的ethtool配置,ARM-Linux驱动--DM9000网卡驱动分析(四)

    原标题:ARM-Linux驱动--DM9000网卡驱动分析(四) 硬件平台:FL2440 (S3C2440) 内核版本:2.6.35 主机平台:Ubuntu 11.04 内核版本:2.6.39 交叉编 ...

  10. USB 网卡驱动数据流

    1. peripheral 1.1 发数据 tx (1)应用层通过系统调用,进入到内核层: (2)内核的数据链路层将数据送入驱动层: (3)USB 网卡驱动将数据发送到 UDC控制器:(将req 写入 ...

最新文章

  1. 新的机器学习特性包含Python
  2. 计算机仿真技术与应用,[计算机软件及应用]计算机仿真技术与应用简介.pdf
  3. Struts2.0实现的文件上传(单附件和多附件)以及附件下载功能
  4. Hadoop的版本介绍
  5. leetcode 904:水果成篮(滑动窗口)
  6. md5 java .net_.net, java MD5 加密 互换
  7. mysql workbench中文设置 mac系统,win系统,linux系统
  8. 学习 shell —— 条件判断 if 的参数
  9. 通过docker-composer启动容器nginx,并完成spring.boot的web站点端口转发
  10. 问题四:C++中inline是干嘛用的
  11. 这10道大厂Java面试题,我敢打赌90%的人都不会!!!
  12. json字符串转json对象
  13. (21)tar打包命令详解
  14. 计算机网络的通信主体,计算机网络试题及答案
  15. FrameMaker 格式的本地化流程(续1)
  16. 走进西藏――53个最基本的常识
  17. 不租服务器,自建个人商业网站(1)
  18. 用Python语言绘制股市OBV指标效果
  19. 从零开始学习D2-Admin,只要学习,任何时候都不晚!
  20. 医学图像处理与分析:现有的各种医学成像手段及其优缺点

热门文章

  1. PhotoShop制作gif动态广告效果示例
  2. 牛课-跳跃游戏1,2,3
  3. win7计算机高级还原,win7怎么恢复出厂设置 win7恢复出厂设置方法【图文】
  4. 如何把win7官方补丁集成到win7iso镜像中
  5. 【仿人机器人】机器人基础介绍
  6. Unity 模拟鼠标点击
  7. html设置鼠标指针的形状,CSS - 鼠标指针样式详解(cursor光标样式、自定义指针图片)...
  8. 每日作业-品优购详情页
  9. linux 快速 命令快捷键,Linux的150个命令和快捷键 整理
  10. Comsol多孔介质内的粒子流动案例,可以追踪粒子运动轨迹