linux usb usbip驱动详解(二)

      终于来到usbip驱动代码分析了!

      我们在做产品时,通常是先讨论方案、制定协议、编码和测试。

      usbip的方案是行得通的,它是从URB对象获取信息,然后从tcp发送出去的,URB是linux usb子系统里面用于抽象usb通信而精心设计的对象,只要server和client两边在恰当的时机分别隔断各自系统的usb通信流程,然后巧妙地交换数据,各自系统都察觉不到,就像黑客利用钩子函数做rookit。如果server和client都是linux系统,就很容易理解了,毕竟部门内的兄弟好说话,跨部门沟通就没那么好了。譬如是linux和windows的话,那么linux的urb就需要转换为windows那边能辨认的的“urb”了,它们的结构体成员肯定不一样,毕竟操作系统都不一样,不过够信息组包就好了。

      usbip有一套协议,用于解决上述问题。

      协议文档在kernel源码根目录下的drivers/usb/usbip/usbip_protocol.txt,比较新的版本在Documentation/usb/usbip_protocol.txt

      在描述usbip原理前,我们先回顾下usb主机控制器和usb驱动(接口驱动)的关系。可以通过阅读usb-skeleton.c代码来学习USB设备驱动是怎么写的。简单来说,当主机控制器驱动里的hub.c检测到root hub的port有插入设备(譬如检测到D-/D+管脚被hub拉低),主机控制器hcd就会发出“获取设备描述符”的请求进行枚举设备,获取到PID/.VID,接口描述符、端口描述符等信息后,报给上层驱动(usb core),usb core会根据PID/VID来match设备驱动,此时假设匹配到usb-skeleton驱动,那么usb-skeleton驱动的probe()被回调,这时就可以进行usb通信了,如U盘读写,键鼠操作等,这些通信是通过填充urb结构体,然后交给usb core模块导出的usb_submit_urb函数,它会回调从主机控制器驱动hcd的struct hc_driver注册到的urb_enqueue回调函数和urb_dequeue回调函数。这两个函数是比较底层的,直接控制usb控制器的寄存器,开启dma传输,最后编码成usb硬件层的“非归零反转码”,用差分电信号传出去。其中usb core模块是linux的大牛们封装好的很多通用的api供我们使用,减少了我们开发usb驱动的难度,因为大部分事情已经交给usb core做了,很多时候就是在框架内注册回调方法。

       对于usbip,假设在client端(vhci-hcd)安装有U盘驱动,vhci-hcd的一个重要作用是虚拟出一个usb主机控制器(hcd),linux很多虚拟设备,譬如虚拟网卡(driver/net/dummy.c)等,根据上面的描述,我们可以知道usb主机控制器的作用,就是枚举信息,以及获取U盘驱动组装的usb通信数据转发给底层硬件,最后给到U盘,跟U盘通信。所以虚拟hcd也具有这样的功能,我们在上一篇文章中知道,client端是使用“usbip attach -r 192.168.100.191 -b 2-1.1”把usb设备attach到本地的,其实这个工具的attach操作就是类似于usb的“热插拔”,踢一下vhci-hcd虚拟出来的主机控制器的root hub(任何主机控制器都有一个根hub),这时hub.c就以为有真实的usb设备插入,按照usb hub的协议发出请求,譬如询问root hub究竟是哪个port口插入了设备等,下面是一些hub需要处理的请求,具体功能参考《usb2.0规范》的CH11章节:

ClearHubFeature    
ClearPortFeature
GetHubDescriptor
GetHubStatus        
GetPortStatus        
SetHubFeature        
SetPortFeature   

       只要我们模拟出root hub端口号以及端口状态值给hub.c,就能蒙骗它,让它以为真的有硬件插入,此时hub.c就会发出枚举usb设备的“请求设备描述符”给root hub,最后给到urb_enqueue,vhci-hcd就是实现一个vhci_urb_enqueue,并注册到struct hc_driver对象的.urb_enqueue成员函数里,vhci_urb_enqueue的功能不是像真实主机控制器驱动那样,操控寄存器,操控DMA,而是通过socket发送出去,给server端的真实主机控制器那边接收,由于server端(usbip-host)真的存在有usb主机控制器,所以把从client(vhci-hcd)的socket发出来的usb数据接收到,重新组装好urb,通过usb core模块的usb_submit_urb接口传给真实的主机控制器里,就能跟接在host端pc的U盘通信了,既然链路已经通了,client端的U盘驱动的其他操作(写入U盘数据或者读取U盘里的文件等)就能按照上面的链路走了,能完全操控host端的真实的U盘了!

      文字很啰嗦,不知道有没有讲明白,但讲解usbip协议前必须要给大家一个初步的原理介绍。

热门文章

暂无图片
编程学习 ·

ASP.NET Core2.0项目实战-003

Views布局(Layouts)视图组件(ViewComponents)分部视图(PartialViews)HTML助手(HtmlHelpers) 页面标签的辅助类Tag助手(TagHelpers)配置全部视图(Global view configutation)视图找的时候如果home里面没有还会找shared文件夹里面的using System; using System.Colle…
暂无图片
编程学习 ·

几种常见的分布式事务解决方案对比

背景 分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免。 ACID 指数据库事务正确执行的四个基本要素: 原子性(Atomicity) 一致性(Consistency) 隔离性(Isolation) 持久性(Durabili…
暂无图片
编程学习 ·

缓存雪崩,缓存穿透,缓存击穿出现的原因及解决方案

缓存雪崩 出现过程假设有如下一个系统,高峰期请求为5000次/秒,4000次走了缓存,只有1000次落到了数据库上,数据库每秒1000的并发是一个正常的指标,完全可以正常工作,但如果缓存宕机了,或者缓存设置了相同的过期时间,导致缓存在同一时刻同时失效,每秒5000次的请求会全部…
暂无图片
编程学习 ·

一些个人感觉很不错的特效

html5 canvas绘制3D森林场景拖动特效https://www.mk2048.com/demo/demo_target_desc_h0ccka0cib.html基于canvas 卡通风格的3D森林场景拖动旋转展示特效。video.js在线视频播放器插件点击》video.js在线视频播放器插件html5 video.js mp4视频播放器插件支持点击全屏、播放 spac…
暂无图片
编程学习 ·

使用MicroPython计算任意位数圆周率

计算任意精度的圆周率是个有趣的主题,得益于python的强大计算能力,我们在MicroPython中也可以轻松的计算pi的数值。先输入下面的代码:""" 文件:pi.py 说明:用MicroPython计算任意精度圆周率计算 作者:未知 版本: 时间: 修改:邵子扬2016.5v1.1 http://b…
暂无图片
编程学习 ·

Java学习 | 基础知识 - 关键字和标识符

Java基本知识 1、关键字关键字不能用于常量、变量、和任何标识符的名称Java关键字整理类别关键字 说明访问控制private私有的protected受保护的public公共的default默认类、方法和变量修饰符abstract声明抽象class类extends扩充,继承final最终值,不可改变的implements实现(接…
暂无图片
编程学习 ·

2020.7.1总结

前端知识: API: 判断是否为空: $.common.isEmpty() modal框: $.common.alertError() 弹层组件:layer layer.open({ title:false, type:1, closeBth:true, shadeClose:true,//阴影区域关闭 area:[‘auto’,‘atuo’]//宽,高 }) 以下是一些参数截图:layer组件:web弹层组…
暂无图片
编程学习 ·

数据库---常用数据库的驱动程序

Oracle数据库 驱动程序包名:ojdbc6.jar 驱动类的名字:oracle.jdbc.driver.OracleDriverJDBC URL:jdbc:oracle:thin: @dpip:port: databasename 说明:驱动程序包名有可能会变 JDBC URL中各个部分含义如下: dbip –为数据库服务器的IP地址,如果是本地可写:localhost或127.…
暂无图片
编程学习 ·

【Docker】 Docker pull的时候指定仓库

1.概述 默认情况下docker pull会从docker hub拉取镜像文件,也可以手动指定一个仓库地址拉取镜像。假如你设置了一个本地仓库地址,那么你只要指定这个地址拉取镜像即可。仓库地址类似一个URL,但是没有协议头http://。 例如从一个镜像地址:myregistry.local:5000,拉取镜像文…
暂无图片
编程学习 ·

冒泡排序

冒泡排序 public class Test{public static void main(String[] args){int[] arr = {20,5,15,10,0,25};int temp = 0;for (int j=0;j<arr.length-1;j++ ){for (int i=0;i < arr.length-1-i ;i++ ){if (arr [i] > arr [i+1]){temp = arr [i+1];arr [i+1] = arr [i];arr…
暂无图片
编程学习 ·

CUDA学习(四):cudaMalloc、cudaMemcpy和cudaFree函数

文章目录一、cudaMalloc、cudaMemcpy和cudaFree 介绍二、第一个例子,实现GPU端的加法 可以像调用C函数那样将参数传递给核函数 当设备执行任何有用的操作时,都需要分配内存,例如将计算机返回给主机。 一、cudaMalloc、cudaMemcpy和cudaFree 介绍 内存空间开辟、内存复制和内…
暂无图片
编程学习 ·

Effective-Java 第三版中文版 77. 不要忽略异常

77. 不要忽略异常 尽管这条建议看上去是显而易见的,但是它却常常被违反,因而值得再次提出来。当 API 的设计者声明一个方法将抛出某个异常的时候,他们等于正在试图说明某些事情。所以,请不要忽略它!要忽略一个异常非常容易,只需将方法调用通过 try 语句包围起来,并包含一…
暂无图片
编程学习 ·

Linux下C语言编程概述

1Linux下C语言编程概述3.1.1C语言简单回顾 C语言最早是由贝尔实验室的DennisRitchie为了UNIX的辅助开发而编写的,它是在B语言的基础上开发出来的。尽管C语言不是专门针对UNIX操作系统或机器编写的,但它与UNIX系统的关系十分紧密。由于它的硬件无关性和可移植性,使C语言逐渐…
暂无图片
编程学习 ·

谷粒商城-rabbitmq

1. RabbitMQ 1.1. 现实问题 目前我们已经完成了商品和搜索系统的开发。我们思考一下,是否存在问题?商品的原始数据保存在数据库中,增删改查都在数据库中完成。 搜索服务数据来源是索引库,如果数据库商品发生变化,索引库数据不能及时更新。如果我们在后台修改了商品的价格…
暂无图片
编程学习 ·

node.js学习

node server.js 开启服务 const http = require(http); const fs = require(fs);let server = http.createServer(function(request, response) {let {url} = request;fs.readFile(www/+url,(err,data) => {if (err){response.write(404)}else{response.write(data)}respons…