一、前言

  1、首先了解一下EHCI、UHCI和OHCI。

  从硬件上来说,usb 设备要想工作,除了外设本身,必须依赖于 usb host controller.一般来说,一个电脑里有一个 usb host controller就可以了,她就可以控制很多个设备了,比如 u 盘,比如 usb 键盘,比如 usb 鼠标.所 有  的外设都把自己的请求提交给usb host controller.然后让 usb host controller 统一来调度.
  现在一般的USB桥接器模块有两种类型,UHCI和OHCI。在决定插入那一个桥接器模块时,可以察看/proc/pci文件来决定。一般而言,UHCI类型的桥接器它的插入模块是uhci或usb-uhci(由内核版本决定);而对于OHCI类型的桥接器它的插入模块是ohci或usb-ohci。

  uhci(universal host controller interface): Intel用在自家芯片组上的usb 1.1主控制器(host controller)的硬件实例
  ehci(enhanced host controller interface): usb 2.0的主控制器标准接口。
  ohci(open host controller inferface):一个不仅仅是usb用的主控制器接口标准。主要是遵循csr (configuration space register)标准。是其他厂商在设计usb host controller时遵循的标准,如via, nec, ali, 包括nvidia等等。
  ehci是满足usb 2.0 specification里面对usb host controller (high speed)的要求的硬件设计。

  就是主机控制器的接口。从硬件上来说,USB 设备要想工作,除了外设本身,必须还有一个 USB 主机控制器。一般来说,一个电脑里有一个 USB 主机控制器就可以了,它就可以控制很多个设备了,比如 U 盘,USB 键盘,USB 鼠标。所有的外设都把自己的请求提交给 USB主机控制器。然后让 USB 主机控制器统一来调度。而设备怎么连到主机控制器上?哎,这就是我们故事的主角,Hub,“乳名”叫做集线器。

  2、准备工作

  linux内核源码下载,可以根据自己喜好选择对应的版本。

  wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.145.tar.gz

  以usb_hub,来了解usb工作

  localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # wc -l hub.c

二、API

  1、Root_Hub

   

  USB 设备的初始化都是 Hub 这边发起的,通常我们写 USB 设备驱动程序都是在已经得到了一个 struct usb_interface 指针的情况下开始 probe 工作。可是我要问你,你的 struct usb_interface 从哪来的?老实说,要想知道从 USB 设备插入 USB 口的那一刻开始,这个世界发生了什么,你必须知道 Hub 是怎么工作的,Linux 中 Hub 驱动程序是怎么工作的。要说 USB Hub,那得从 Root Hub 说起。什么是 Root Hub?冤有头债有主,不管你的电脑里连了多少个 USB 设备,它们最终是有根的。所有的 USB 设备最终都是连接到了一个叫做 Root Hub 的设备上,或者说所有的根源都是从这里开始的。  

  Root Hub 上可以连接别的设备,可以连接 U 盘,可以连接 USB 鼠标,同样也可以连接另一个 Hub。所谓 Hub,就是用来级连。但是普通的 Hub,它一头接上级 Hub,另一头可以有多个口,多个口就可以级连多个设备,也可以只有一个口,一个口的就像大学宿舍里常用的那种延长线。而 Root Hub 呢?它比较特殊,它当然也是一种 USB 设备,但是它属于一人之下万人之上的角色,她只属于主机控制器,换言之,通常做芯片的人会把主机控制器和 Root Hub 集成在一起。特别是 PC 主机上,通常你就只能看到接口,看不到 Root Hub,因为她在主机控制器里。

  2、usb_init

  综上所述,整个USB子系统在初始化的时候,会先执行初始化Root Hub。

  来自drivers/usb/core/usb.c:

 982 static int __init usb_init(void)
983 {
......
1007 if (retval)
1008 goto usb_devio_init_failed;
1009 retval = usb_hub_init();
......
1032 }

  在众多名叫*init*的函数之中,是有一个属于 Hub 的,它在这里初始化,换言之,随着 USB Core 的初
始化,Hub 也就开始了它的初始化之路,usb_hub_init()函数得到调用。

  来自drivers/usb/core/hub.c:

5600 int usb_hub_init(void)
5601 {
5602 if (usb_register(&hub_driver) < 0) {
5603 printk(KERN_ERR "%s: can't register hub driver\n",
5604 usbcore_name);
5605 return -1;
5606 }
5607
5608 /*
5609 * The workqueue needs to be freezable to avoid interfering with
5610 * USB-PERSIST port handover. Otherwise it might see that a full-speed
5611 * device was gone before the EHCI controller had handed its port
5612 * over to the companion full-speed controller.
5613 */
5614 hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
5615 if (hub_wq)
5616 return 0;
5617
5618 /* Fall through if kernel_thread failed */
5619 usb_deregister(&hub_driver);
5620 pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name);
5621
5622 return -1;
5623 }

  usb_register()这个函数是用来向 USB 核心层,即 USB Core,注册一个 USB 设备驱动的,而这里我们注册的是注册的是 Hub 的驱动程序所对应的 struct usb_driver 结构体变量。定义于 drivers/usb/core/hub.c 中。

5586 static struct usb_driver hub_driver = {
5587 .name = "hub",
5588 .probe = hub_probe,
5589 .disconnect = hub_disconnect,
5590 .suspend = hub_suspend,
5591 .resume = hub_resume,
5592 .reset_resume = hub_reset_resume,
5593 .pre_reset = hub_pre_reset,
5594 .post_reset = hub_post_reset,
5595 .unlocked_ioctl = hub_ioctl,
5596 .id_table = hub_id_table,
5597 .supports_autosuspend = 1,
5598 };

  这里面最重要的一个函数就是 hub_probe,很多事情都在这期间发生了。每个 USB 设备(或者说所有设备)的驱动都会有一个 probe 函数,比如 U 盘的 probe 函数就是 storage_probe(),不过 storage_probe()被调用需要两个前提,第一个 usb-storage 被加载了,第二个 U 盘等设备插入了被检测到了。而 Hub,说它特别,我可绝不是忽悠你。Hub 本身就有两种,一种是普通的 Hub,一种是 Root Hub。对于普通 Hub,它完全可能也是和 U 盘一样,在某个时刻被你插入,然后这种情况下 hub_probe 被调用,但是对于 Root Hub 就不需要这么多废话了,Root Hub 肯定是有的,只要你有 USB 主机控制器,就一定会有 Root Hub,所以 hub_probe()基本上是很自然地就被调用了,不用说非得等待某个插入事件的发生,没这个必要。当然没有 USB 主机控制器就没有 USB 设备能工作。那么 USB Core 这整个模块你就没有必要分析了。所以,只要你有 USB 主机控制器,那么在 USB 主机控制器的驱动程序初始化的过程中,它就会调用 hub_probe()来探测 RootHub,不管你的主机控制器是 OHCI、UHCI 还是 EHCI 的接口。如果 register 一切顺利的话,那么返回值为 0。如果返回值为负数,就说明出错了。现在假设这一步没有出错。

  Ok,usb_hub_init()的5614行,这行代码其实是很有技术含量的,不过对于写驱动的人来说,其作用就和当年的kernel_thread()相当。不过kernel_thread()返回值是一个int型的,而kthread_run()返回的却是structtask_struct 结构体指针。这里等号左边的 khubd_task 是我们自己定义的一个 struct task_struct 指针。

  56 /* workqueue to process hub events */
57 static struct workqueue_struct *hub_wq;
58 static void hub_event(struct work_struct *work);

  定义与 kernel/workqueue.c

 235 /*
236 * The externally visible workqueue. It relays the issued work items to
237 * the appropriate worker_pool through its pool_workqueues.
238 */
239 struct workqueue_struct {
240 struct list_head pwqs; /* WR: all pwqs of this wq */
241 struct list_head list; /* PR: list of all workqueues */
242
243 struct mutex mutex; /* protects this wq */
244 int work_color; /* WQ: current work color */
245 int flush_color; /* WQ: current flush color */
246 atomic_t nr_pwqs_to_flush; /* flush in progress */
247 struct wq_flusher *first_flusher; /* WQ: first flusher */
248 struct list_head flusher_queue; /* WQ: flush waiters */
249 struct list_head flusher_overflow; /* WQ: flush overflow list */
250
251 struct list_head maydays; /* MD: pwqs requesting rescue */
252 struct worker *rescuer; /* I: rescue worker */
253
254 int nr_drainers; /* WQ: drain in progress */
255 int saved_max_active; /* WQ: saved pwq max_active */
256
257 struct workqueue_attrs *unbound_attrs; /* PW: only for unbound wqs */
258 struct pool_workqueue *dfl_pwq; /* PW: only for unbound wqs */
259
260 #ifdef CONFIG_SYSFS
261 struct wq_device *wq_dev; /* I: for sysfs interface */
262 #endif
263 #ifdef CONFIG_LOCKDEP
264 char *lock_name;
265 struct lock_class_key key;
266 struct lockdep_map lockdep_map;
267 #endif
268 char name[WQ_NAME_LEN]; /* I: workqueue name */
269
270 /*
271 * Destruction of workqueue_struct is RCU protected to allow walking
272 * the workqueues list without grabbing wq_pool_mutex.
273 * This is used to dump all workqueues from sysrq.
274 */
275 struct rcu_head rcu;
276
277 /* hot fields used during command issue, aligned to cacheline */
278 unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */
279 struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */
280 struct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */
281 };

  5615行,判断hub_wq,

  IS_ERR 是一个宏,用来判断指针的。当你创建了一个进程,你当然想知道这个进程创建成功了没有。以前我们注意到每次申请内存时都会做一次判断,你说创建进程是不是也要申请内存?不申请内存谁来记录 struct task_struct?很显然,要判断。以前我们判断的是指针是否为空。以后接触代码多了你会发现,其实 Linux 内核中有很多种内存申请的方式,而这些方式所返回的内存地址也是不一样的,所以并不是每一次我们都只要判断指针是否为空就可以了。事实上,每一次调用 kthread_run()之后,我们都会用一个IS_ERR()来判断指针是否有效。IS_ERR()为 1 就表示指针有错,或者准确一点说叫做指针无效。

  如果 IS_ERR()返回值是 0,那么说明没有问题,于是返回值为 0,也就是说 usb_hub_init()就这么结束了。反之,就会执行 usb_deregister()。

最新文章

  1. asp.net core 简单部署
  2. jquery easyui tree动态加载子节点
  3. C语言学习018:strdup复制字符串数组
  4. oracle: tochar(sysdate,&#39;D&#39;)函数
  5. JavaWeb项目前端规范(采用命名空间使js深度解耦合)
  6. 学一点Git--20分钟git快速上手 [Neil]
  7. SQL SERVER定时任务执行跟踪--供远程查看 [原创]
  8. CentOS笔记——配置DNS服务器
  9. Linux学习笔记20——第一个多线程程序
  10. 分享一个md5类
  11. VSTO之旅系列(二):创建Excel解决方案
  12. 摘抄来自论坛的一些DDD讨论
  13. NVIC
  14. Eclipse常用的6个Debug技巧
  15. WPF 完美截图 &lt;一&gt;
  16. 在ARC下,assign和weak的区别
  17. Dynamics CRM2015 2015版本可用的OData Query Designer工具
  18. Spring JTA multiple resource transactions in Tomcat with Atomikos example
  19. linux基础之awk
  20. .net core2 mvc项目中,加入RazorPages页面

热门文章

  1. 找vector最大最小《转载》
  2. db2存储过程 动态拼接sql 、输出数据集示例
  3. 【转】【善用佳软】文件复制软件评测:FastCopy、TeraCopy、ExtremeCopy、Supercopier
  4. python常用的六个字符串处理方法
  5. vs MFC c++ rc文件的dilog打不开,显示加载失败
  6. uni-app 声音/震动提示,播放系统默认消息声音 安卓(Android)测试通过
  7. Laravel 5.1 LTS 速查表
  8. ThinkPHP获取当前url
  9. SWUpdate(Suricatta) + Hawkbit Server
  10. ncnn 加载 bin文件时,出错 报异常 0xC0000094:Integer division by zero。