xHci-PCI驱动设计

虽然Linux内核拥有C语言构建的身体,但它骨子里散发的是面向对象的气质,这一个个的对象就是struct。面对一个内核模块的时候,首先要找出关键的struct和它们之间的关系,才能摸清代码的骨骼脉络。

先上图,下面分析的过程结束之后各个结构体就是这种关系:

重要的配置:

xHCI是USB 3.x的host controller规范,首先进入drivers/usb/host目录浏览一下,其实从文件名就可以知道,跟xHCI关系最密切的必然是xhci.c。保险起见还是看一下KConfig:

config USB_XHCI_HCD 
   tristate "xHCI HCD (USB 3.0)support" 
   ---help--- 
      TheeXtensible Host Controller Interface (xHCI) is standard forUSB 3.0 
      "SuperSpeed"host controller hardware. 
 
      Tocompile this driver as a module, choose M here: the 
     module will be called xhci-hcd. 
 
ifUSB_XHCI_HCD 
 
config USB_XHCI_PCI 
      tristate 
      depends on PCI 
       defaulty 
 
config USB_XHCI_PLATFORM 
   tristate 
 
config USB_XHCI_MVEBU 
   tristate "xHCI support forMarvell Armada 375/38x" 
   select USB_XHCI_PLATFORM 
   depends on ARCH_MVEBU || COMPILE_TEST 
   ---help--- 
      Say‘Y‘to enable the support for the xHCI host controller 
     found in Marvell Armada 375/38x ARM SOCs. 
 
config USB_XHCI_RCAR 
   tristate "xHCI support forRenesas R-Car SoCs" 
   select USB_XHCI_PLATFORM 
   depends on ARCH_SHMOBILE || COMPILE_TEST 
   ---help--- 
      Say‘Y‘to enable the support for the xHCI host controller 
     found in Renesas R-Car ARM SoCs. 
 
endif # USB_XHCI_HCD

主要配置项叫做USB_XHCI_HCD,另外还有USB_XHCI_PCI和USB_XHCI_PLATFORM。再看Makefile,直接搜索XHCI有关的内容:

xhci-hcd-y := xhci.oxhci-mem.o 
xhci-hcd-y += xhci-ring.o xhci-hub.oxhci-dbg.o 
xhci-hcd-y += xhci-trace.o 
 
xhci-plat-hcd-y := xhci-plat.o 
ifneq ($(CONFIG_USB_XHCI_MVEBU), ) 
   xhci-plat-hcd-y     +=xhci-mvebu.o 
endif 
ifneq ($(CONFIG_USB_XHCI_RCAR), ) 
   xhci-plat-hcd-y     +=xhci-rcar.o 
endif 
 
obj-$(CONFIG_USB_XHCI_PCI)  += xhci-pci.o 
obj-$(CONFIG_USB_XHCI_PLATFORM) +=xhci-plat-hcd.o 
 
obj-$(CONFIG_USB_XHCI_HCD)  += xhci-hcd.o

USB_XHCI_PCI,顾名思义,是xHCI驱动和PCI总线驱动之间的“接口”(内核开发者称这种“接口”为glue)。USB控制器大多是PCI设备,若控制器连接到PCI总线上,那么自然是先由PCI驱动发现该设备,识别之后才能交给xHCI驱动处理。所以实际上,作为glue的xhci-pci模块代码要早于xhci-hcd模块代码开始工作,因此关键的初始化过程放在xhci-pci里面。

现在进入正题:

xHCI相关的代码,很容易发现几个貌似重要的struct类型:usb_hcd、xhci_hcd和hc_driver,还有几个全局变量xhci_pci_driver、xhci_hc_driver和xhci_pci_hc_driver,再加上PCI总线相关的类型pci_dev和pci_driver。

xhci-pci模块启动,执行xhci_pci_init函数

staticint__init xhci_pci_init(void)
{
   xhci_init_driver(&xhci_pci_hc_driver, xhci_pci_setup);
#ifdef CONFIG_PM
   xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend;
   xhci_pci_hc_driver.pci_resume = xhci_pci_resume;
#endif
    returnpci_register_driver(&xhci_pci_driver);
}

xhci_pci_init调用xhci_init_driver,初始化xhci_pci_hc_driver变量,主要作用就是把全局变量xhci_hc_driver的值一股脑赋给另一个全局变量xhci_pci_hc_driver。两者都是struct hc_driver类型,xhci_pci_hc_driver在xhci-pci.c中定义,是真正起作用的xHCI驱动,但它在定义的时候没有进行任何成员的初始化:

void xhci_init_driver(struct hc_driver *drv, int(*setup_fn)(struct usb_hcd *))
{
   BUG_ON(!setup_fn);
    *drv= xhci_hc_driver;
    drv->reset= setup_fn;
}

staticstructhc_driver __read_mostly xhci_pci_hc_driver;

而xhci_hc_driver在xhci.c中定义,它包揽了所有的脏活累活:

staticconststruct hc_driver xhci_hc_driver = {
   .description =      "xhci-hcd",
   .product_desc =     "xHCI Host Controller",
    .hcd_priv_size=    sizeof(structxhci_hcd *),

/*
     *generic hardware linkage
     */
    .irq=          xhci_irq,
   .flags =        HCD_MEMORY |HCD_USB3 | HCD_SHARED,

/*
     *basic lifecycle operations
     */
   .reset =        NULL, /* set in xhci_init_driver() */
   .start =        xhci_run,
    .stop=         xhci_stop,
   .shutdown =     xhci_shutdown,

/*
     *managing i/o requests and associated device resources
     */
   .urb_enqueue =     xhci_urb_enqueue,
   .urb_dequeue =     xhci_urb_dequeue,
   .alloc_dev =       xhci_alloc_dev,
   .free_dev =     xhci_free_dev,
   .alloc_streams =   xhci_alloc_streams,
   .free_streams =    xhci_free_streams,
   .add_endpoint =    xhci_add_endpoint,
    .drop_endpoint=    xhci_drop_endpoint,
   .endpoint_reset =  xhci_endpoint_reset,
   .check_bandwidth = xhci_check_bandwidth,
   .reset_bandwidth = xhci_reset_bandwidth,
   .address_device =  xhci_address_device,
   .enable_device =   xhci_enable_device,
   .update_hub_device =   xhci_update_hub_device,
   .reset_device =    xhci_discover_or_reset_device,

/*
     *scheduling support
     */
   .get_frame_number = xhci_get_frame,

/*
     *root hub support
     */
   .hub_control =      xhci_hub_control,
   .hub_status_data = xhci_hub_status_data,
   .bus_suspend =     xhci_bus_suspend,
   .bus_resume =      xhci_bus_resume,

/*
     *call back when device connected and addressed
     */
   .update_device =        xhci_update_device,
   .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm,
   .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
   .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
   .find_raw_port_number = xhci_find_raw_port_number,
};

xhci_init_driver函数将xhci_hc_driver的值赋给xhci_pci_hc_driver后,前者也就退下了舞台。

xhci_pci_driver被专业人员称为pcidriver glue,属于一种新型的PCI驱动模块。

xhci_pci_init调用pci_register_driver,将xhci_pci_driver注册为PCI设备驱动。在xhci-pci.c中静态定义并初始化:

/* pci driver glue; this is a"new style" PCI driver module */
staticstructpci_driver xhci_pci_driver = {
    .name=     (char*) hcd_name,
   .id_table = pci_ids,

.probe =    xhci_pci_probe,
   .remove =   xhci_pci_remove,
    /* suspend and resume implemented later */

.shutdown =    usb_hcd_pci_shutdown,
#ifdef CONFIG_PM
   .driver = {
       .pm = &usb_hcd_pci_pm_ops
    },
#endif
};

id_table包含了驱动支持的PCI设备类型,PCI总线就是靠它判断驱动和设备能否配对。这里的id_table成员设置为pci_ids,它也是静态定义的全局变量:

/* PCI driver selectionmetadata; PCI hotplugging uses this */
staticconststructpci_device_id pci_ids[] = { {
    /* handle any USB 3.0 xHCI controller */
   PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
   .driver_data =  (unsignedlong)&xhci_pci_hc_driver,
    },
    { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, pci_ids);

pci_ids中唯一一个元素的driver_data成员指向刚才在xhci_pci_init中完成初始化的xhci_pci_hc_driver变量,这就将作为PCI设备驱动的xhci_pci_driver和作为USB主机控制器设备驱动xhci_pci_hc_driver联系了起来。

staticintxhci_pci_setup(struct usb_hcd *hcd)
{
        structxhci_hcd         *xhci;
        structpci_dev          *pdev =to_pci_dev(hcd->self.controller);
        int                     retval;
       //uDP720202芯片的配置
       xhci_fwdownload(hcd);
        //创建了对xHCI至关重要的数据结构——xhci_hcd类型,并完成了大量的初始化工作
       retval = xhci_gen_setup(hcd, xhci_pci_quirks);
        if(retval)
               return retval;

xhci = hcd_to_xhci(hcd);
        if(!usb_hcd_is_primary_hcd(hcd))
               return0;

pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
       xhci_dbg(xhci, "GotSBRN %u\n", (unsignedint)xhci->sbrn);

/* Find any debug ports */
       retval = xhci_pci_reinit(xhci, pdev);
        if(!retval)
               return retval;

kfree(xhci);
        returnretval;
}

USB 3.1 Spec中Figure 3-1可以知道,USB 3.x的Host包含两个roothub,对应两条USB总线,一条连接USB 2.0设备,一条连接USB 3.x设备。

xhci_pci_probe调用usb_hcd_pci_probe,创建usb_hcd和xhci_hcd。回到最初的示意图,现在所有的连接都已经完成!

int usb_hcd_pci_probe(struct pci_dev *dev, conststruct pci_device_id *id)
{
    structhc_driver        *driver;
    structusb_hcd          *hcd;
    int                     retval;
    int                     hcd_irq = 0;

if(usb_disabled())
           return-ENODEV;

if(!id)
           return-EINVAL;
    driver= (structhc_driver *)id->driver_data;
    if(!driver)
           return-EINVAL;

if(pci_enable_device(dev) < 0)
           return-ENODEV;

/*
     * The xHCI driver has its own irq management
     * make sure irq setup is not touched for xhciin generic hcd code
     */
    if((driver->flags & HCD_MASK) != HCD_USB3) {
           if(!dev->irq) {
                  dev_err(&dev->dev,
                  "Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
                         pci_name(dev));
                  retval= -ENODEV;
                  gotodisable_pci;
           }
           hcd_irq= dev->irq;
    }

hcd =usb_create_hcd(driver, &dev->dev, pci_name(dev));
    if(!hcd) {
           retval= -ENOMEM;
           gotodisable_pci;
    }
    ......
    returnretval;
}

uDP720202芯片的配置的时序图:

//@@----------------------PCI 信息
#define XHCI_FWFILENAME_720201_202ES20  "renesas/K2011070.mem"
//uDp720202 寄存器配置
#define XHCI_VENDOR_ID_720202           (0x1912)
#define XHCI_DEVICE_ID_720202           (0x0015)
#define XHCI_DEVICE_REV_ES20            (0x02)

//@@---------------------- FM下载配置寄存器
#define PCICNF0F4  0xF4  // 来自芯片寄存器手册
   /*
   0 = FWdownload enable (1), RW
   1 = FWdownload lock (1) or unlock (0), need 0 to perform download, RW(Write Once)
   6:4 =Result code, RO -- processing (0), success (1), error (2),
   8 =Set Data 0, RW
   9 =Set Data 1, RW
   16:31= (used for serial EEPROM read/write.  31= serial EEPROM present.)
   */
#define PCICNF0F4_FWDOWNLOADENABLE_B    (0)
#define PCICNF0F4_FWDOWNLOADLOCK_B      (1)
#define PCICNF0F4_SETDATA0_B            (8)
#define PCICNF0F4_SETDATA1_B            (9)
#define PCICNF0F4_RESULT_B              (4)

#define PCICNF0F8  0xF8  // 数据页 0

#define PCICNF0FC  0xFC  // 数据页 1

主要调用过程:

时间: 2024-08-08 22:37:09

xHci-PCI驱动设计的相关文章

领域驱动设计-入门

领域驱动设计围绕着对象进行设计,类似于传统的OO,但是还是不同的. 传统的OO更像是贫血的领域对象,它具有数据,很多get set方法,但是缺少业务逻辑.客户端使用时,需要进行一大串的set操作.举个栗子: 这种方式中customer是一个贫血的领域对象,客户端必须进行很多的set,最后调用dao进行保存. --------------- 那么,怎样才是不贫血的领域对象呢?主要看两点:boundedcontext,通用领域语言. 领域对象是通过通用领域语言进行描述的,通用语言是领域专家和开发人员

我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践

写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯新湖,鱼跃翠堤:第一次念你,燕飞巢冷,释怀记忆:第一次梦你,云翔海岛,轮渡迤逦:第一次认你,怨江别续,草桥知己:第一次怕你,命悬一线,遗憾禁忌:第一次悟你,千年菩提,生死一起. 人生有很多的第一次:小时候第一次牙牙学语.第一次学蹒跚学步...长大后第一次上课.第一次逃课.第一次骑自行车.第一次懂事.第一次和喜

初学DDD-领域驱动设计

这几天刚开始学习DDD,看了几篇大神的文章,现在只是知道了几个名词,还没有详细的学习.结合自己的工作经历,说说自己的看法,请各位大神多多指点. 最开始用的比较多的是以数据库表建立模型驱动开发.后来发现这种开发方式有很大的弊端:项目开始的时候,对业务分析不够明确,就开始建立数据库表,之后根据建好的表,来设计怎么去实现功能.这就导致开发过程中,经常会有字段冗余,表结构混乱,分工不明确,相似代码太多等问题.接着就会在项目开始之前,有意识的分析业务逻辑,理清楚一个项目的功能模块,拆分不同模块,对每个模块

DDD领域驱动设计基本理论知识总结

领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity in the Heart of Software (领域驱动设计),简称Evans DDD.领域驱动设计分为两个阶段: 以一种领域专家.设计人员.开发人员都能理解的通用语言作为相互交流的工具,在交流的过程中发现领域概念,然后将这些概念设计成一个领域模型:由领域模型驱动软件设计,用代码来实现该领域模型:

(转载)浅谈我对DDD领域驱动设计的理解

原文地址:http://www.cnblogs.com/netfocus/p/5548025.html 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品.所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的. 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备.但是最近由于各种原因,导致服务经常出故

WCF客户端配置以及代理-----基于DDD领域驱动设计的WCF+EF+WPF分层框架(4)

写在最前面:转载请注明出处 目录置顶: 关于项目--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(1) 架构搭建--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(2) WCF服务端具体实现---------基于DDD领域驱动设计的WCF+EF+WPF分层框架(3) WCF客户端配置以及代理-----基于DDD领域驱动设计的WCF+EF+WPF分层框架(4) Domain具体实现------------基于DD

什么是领域驱动设计(Domain Driven Design)?

本文是从 What is Domain Driven Design? 这篇文章翻译而来. ”…在很多领域,专家的作用体现在他们的专业知识上而不是智力上.“ -- Don Reinertsen 领域驱动设计(Domain Driven Design)是一种软件开发方法,目的是让软件系统在实现时准确的基于对真实业务过程的建模并根据真实业务过程的调整而调整. 传统的开发工作趋向于一种以技术为先导的过程,需求从业务方传递到开发团队,开发人员依据需求上的描述创造出最有可能的假想. 在瀑布开发过程中,这导致

领域驱动设计的面向服务架构

[.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店 一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Byteart Retail案例并没有对其形成过程做一步步分析,而是把整个DDD的实现案例展现给我们,这对于一些刚刚接触领域驱动设计的朋友可能会非常迷茫,从而觉得领域驱动设计很难,很复杂,因为学习中要消化一个整个案例的知识,这样未免很多人消化不了就打退堂鼓,就不继续研究下去了,所以这样也不利于DDD的推广.然而本系列

DDD领域驱动设计之领域服务

1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B也不好,因为很可能会涉及多个实体或者聚合的交互(也可能是多个相同类型的实体),此时就应该吧这些代码放到领域服务中,领域服务其实就跟传统三层的BLL很相似,只有方法没有属性,也就没有状态,而且最好是用动词命名,service为后缀,但是真正到了实践的时候,很多时候是很难区分是领域实体本身实现还是用领域