linux xhci源码,xHCI驱动学习(1) 核心数据结构

2023-05-16

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

大致浏览几眼xHCI相关的代码,很容易发现几个貌似重要的struct类型:usb_hcd、xhci_hcd和hc_driver,还有几个全局变量xhci_pci_driver、xhci_hc_driver和xhci_pci_hc_driver,再加上PCI总线相关的类型pci_dev和pci_driver。不要被这些眼花缭乱的名字吓到,今天要做的就是把这些结构之间的关系理顺。下面按照相关代码的执行顺序,看一下这些结构是如何被建立和初始化的。

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

0818b9ca8b590ca3270a3433284dd417.png

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

static int __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

return pci_register_driver(&xhci_pci_driver);

}

1.1 xhci_pci_init调用xhci_init_driver,初始化xhci_pci_hc_driver变量

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

static struct hc_driver __read_mostly xhci_pci_hc_driver;

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

static const struct hc_driver xhci_hc_driver = {

.description = "xhci-hcd",

.product_desc = "xHCI Host Controller",

.hcd_priv_size = sizeof(struct xhci_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,

};

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;

}

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

(free-electrons是个好网站)

0818b9ca8b590ca3270a3433284dd417.png

1.2 xhci_pci_init调用pci_register_driver,将xhci_pci_driver注册为PCI设备驱动

xhci_pci_driver是xHCI控制器作为PCI设备对应的驱动,符合PCI设备驱动的标准类型struct pci_driver,在xhci-pci.c中静态定义并初始化:

/* pci driver glue; this is a "new style" PCI driver module */

static struct pci_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,一个是probe。id_table包含了驱动支持的PCI设备类型,PCI总线就是靠它判断驱动和设备能否配对。这里的id_table成员设置为pci_ids,它也是静态定义的全局变量:

/* PCI driver selection metadata; PCI hotplugging uses this */

static const struct pci_device_id pci_ids[] = { {

/* handle any USB 3.0 xHCI controller */

PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),

.driver_data = (unsigned long) &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联系了起来。本文最开始的图中右半部分画出了它们的关系。

当pci_register_driver调用完成后,xhci_pci_driver就被加入了PCI总线的驱动列表,当总线检测到与之匹配的设备,即xHCI控制器的时候,会调用驱动的probe成员函数,而xhci_pci_driver.probe在初始化时被设置为xhci_pci_probe函数,因此接下来就顺藤摸瓜考察它。

2. PCI设备配对成功,执行xhci_pci_probe函数

这个函数也定义在xhci-pci.c中,比较长(所以加了行号),先贴前半部分:

217 static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)

218 {

219 int retval;

220 struct xhci_hcd *xhci;

221 struct hc_driver *driver;

222 struct usb_hcd *hcd;

223

224 driver = (struct hc_driver *)id->driver_data;

225

226 /* Prevent runtime suspending between USB-2 and USB-3 initialization */

227 pm_runtime_get_noresume(&dev->dev);

228

229 /* Register the USB 2.0 roothub. 230 * FIXME: USB core must know to register the USB 2.0 roothub first. 231 * This is sort of silly, because we could just set the HCD driver flags 232 * to say USB 2.0, but I'm not sure what the implications would be in 233 * the other parts of the HCD code. 234 */

235 retval = usb_hcd_pci_probe(dev, id);

236

237 if (retval)

238 goto put_runtime_pm;

...

注意函数的两个参数,由于是被PCI总线驱动调用的,因此一个是pci_dev结构,一个是pci_device_id结构。224行,程序从pci_device_id结构中取出了driver_data成员。刚才说到xHCI的pci_device_id结构中的driver_data成员指向xhci_pci_hc_driver,因此接下来driver指针都是指向它。

235行,调用usb_hcd_pci_probe函数。这是USB 1.x和2.0的各种控制器驱动使用的probe函数,为什么此处要用它呢?从USB 3.1 Spec中Figure 3-1可以知道,USB 3.x的Host包含两个roothub,对应两条USB总线,一条连接USB 2.0设备,一条连接USB 3.x设备。在xHCI驱动里,需要为每个roothub建立一个usb_hcd结构,而2.0 roothub需要首先创建,因此就直接调用usb_hcd_pci_probe了。

usb_hcd结构类型的定义位于include/linux/usb/hcd.h:

79 struct usb_hcd {

80

81 /* 82 * housekeeping 83 */

84 struct usb_bus self; /* hcd is-a bus */

85 struct kref kref; /* reference counter */

...

从上面可以看到,每个usb_hcd中包含一个usb_bus结构,代表对应的总线。再看下面的代码,每个usb_bus中又包含device结构指针指向对应的设备模型。由于这里的两条USB总线在系统中实际属于同一个物理设备(都对应xHCI控制器这个PCI设备里),因此可以想象同一个xHCI控制器对应的两个usb_bus结构中的device指针应当指向同一处。

usb_bus结构类型的定义位于include/linux/usb.h:

319 /* 320 * Allocated per bus (tree of devices) we have: 321 */

322 struct usb_bus {

323 struct device *controller; /* host/master side hardware */

324 int busnum; /* Bus number (in order of reg) */

325 const char *bus_name; /* stable id (PCI slot_name etc) */

...

2.1 xhci_pci_probe调用usb_hcd_pci_probe,创建usb_hcd和xhci_hcd

回到正题,考察usb_hcd_pci_probe函数:

177 int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)

178 {

179 struct hc_driver *driver;

180 struct usb_hcd *hcd;

181 int retval;

182 int hcd_irq = 0;

183

184 if (usb_disabled())

185 return -ENODEV;

186

187 if (!id)

188 return -EINVAL;

189 driver = (struct hc_driver *)id->driver_data;

190 if (!driver)

191 return -EINVAL;

192

193 if (pci_enable_device(dev) < 0)

194 return -ENODEV;

195

196 /* 197 * The xHCI driver has its own irq management 198 * make sure irq setup is not touched for xhci in generic hcd code 199 */

200 if ((driver->flags & HCD_MASK) != HCD_USB3) {

201 if (!dev->irq) {

202 dev_err(&dev->dev,

203 "Found HC with no IRQ. Check BIOS/PCI %s setup!\n",

204 pci_name(dev));

205 retval = -ENODEV;

206 goto disable_pci;

207 }

208 hcd_irq = dev->irq;

209 }

210

211 hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));

212 if (!hcd) {

213 retval = -ENOMEM;

214 goto disable_pci;

215 }

216

217 hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&

218 driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;

219

220 if (driver->flags & HCD_MEMORY) {

221 /* EHCI, OHCI */

...

237 } else {

238 /* UHCI */

...

257 }

258

259 pci_set_master(dev);

260

261 /* Note: dev_set_drvdata must be called while holding the rwsem */

262 if (dev->class == CL_EHCI) {

263 down_write(&companions_rwsem);

264 dev_set_drvdata(&dev->dev, hcd);

265 for_each_companion(dev, hcd, ehci_pre_add);

266 retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);

267 if (retval != 0)

268 dev_set_drvdata(&dev->dev, NULL);

269 for_each_companion(dev, hcd, ehci_post_add);

270 up_write(&companions_rwsem);

271 } else {

272 down_read(&companions_rwsem);

273 dev_set_drvdata(&dev->dev, hcd);

274 retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);

275 if (retval != 0)

276 dev_set_drvdata(&dev->dev, NULL);

277 else

278 for_each_companion(dev, hcd, non_ehci_add);

279 up_read(&companions_rwsem);

280 }

281

282 if (retval != 0)

283 goto unmap_registers;

284 device_wakeup_enable(hcd->self.controller);

285

286 if (pci_dev_run_wake(dev))

287 pm_runtime_put_noidle(&dev->dev);

288 return retval;

...

为了节约显示器,中间一些与xHCI无关的细节和末尾的错误处理代码就先省略了。这里最关键的,189行中使用和xhci_pci_probe函数中同样的方法令driver指针指向了xhci_pci_hc_driver。211行,调用usb_create_hcd函数创建usb_hcd结构。

2.1.1 usb_hcd_pci_probe调用usb_create_hcd,进而调用usb_create_shared_hcd创建usb_hcd结构

2501 struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,

2502 struct device *dev, const char *bus_name)

2503 {

2504 return usb_create_shared_hcd(driver, dev, bus_name, NULL);

2505 }

2434 struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,

2435 struct device *dev, const char *bus_name,

2436 struct usb_hcd *primary_hcd)

2437 {

2438 struct usb_hcd *hcd;

2439

2440 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);

2441 if (!hcd) {

2442 dev_dbg (dev, "hcd alloc failed\n");

2443 return NULL;

2444 }

2445 if (primary_hcd == NULL) {

2446 hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),

2447 GFP_KERNEL);

2448 if (!hcd->bandwidth_mutex) {

2449 kfree(hcd);

2450 dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");

2451 return NULL;

2452 }

2453 mutex_init(hcd->bandwidth_mutex);

2454 dev_set_drvdata(dev, hcd);

2455 } else {

2456 mutex_lock(&usb_port_peer_mutex);

2457 hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;

2458 hcd->primary_hcd = primary_hcd;

2459 primary_hcd->primary_hcd = primary_hcd;

2460 hcd->shared_hcd = primary_hcd;

2461 primary_hcd->shared_hcd = hcd;

2462 mutex_unlock(&usb_port_peer_mutex);

2463 }

2464

2465 kref_init(&hcd->kref);

2466

2467 usb_bus_init(&hcd->self);

2468 hcd->self.controller = dev;

2469 hcd->self.bus_name = bus_name;

2470 hcd->self.uses_dma = (dev->dma_mask != NULL);

2471

2472 init_timer(&hcd->rh_timer);

2473 hcd->rh_timer.function = rh_timer_func;

2474 hcd->rh_timer.data = (unsigned long) hcd;

2475 #ifdef CONFIG_PM

2476 INIT_WORK(&hcd->wakeup_work, hcd_resume_work);

2477 #endif

2478

2479 hcd->driver = driver;

2480 hcd->speed = driver->flags & HCD_MASK;

2481 hcd->product_desc = (driver->product_desc) ? driver->product_desc :

2482 "USB Host Controller";

2483 return hcd;

2484 }

2440行,创建usb_hcd类型变量。

2445行,由于usb_create_hcd调用usb_create_shared_hcd时primary_hcd参数为NULL,进入if语句的第一个分支。

2454行,令pci_device中内嵌的device结构的driver_data成员指向刚创建的usb_hcd结构。

2468行,令usb_hcd中内嵌的usb_bus结构的controller指针指向device结构。

2479行,令usb_hcd中的driver成员指向xhci_pci_hc_driver。

回到usb_hcd_pci_probe函数,264/273行又设置了一遍driver_data,貌似跟上面重复了。

2.1.2 usb_hcd_pci_probe调用usb_add_hcd,最终调用xhci_gen_setup,创建xhci_hcd结构

2625 int usb_add_hcd(struct usb_hcd *hcd,

2626 unsigned int irqnum, unsigned long irqflags)

2627 {

...

2733 /* "reset" is misnamed; its role is now one-time init. the controller 2734 * should already have been reset (and boot firmware kicked off etc). 2735 */

2736 if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {

2737 dev_err(hcd->self.controller, "can't setup: %d\n", retval);

2738 goto err_hcd_driver_setup;

2739 }

...

usb_add_hcd函数很长,最关键的是它调用了xhci_pci_hc_driver的reset成员,即xhci_pci_setup函数,后者又进一步调用xhci_gen_setup。

187 static int xhci_pci_setup(struct usb_hcd *hcd)

188 {

189 struct xhci_hcd *xhci;

190 struct pci_dev *pdev = to_pci_dev(hcd->self.controller);

191 int retval;

192

193 retval = xhci_gen_setup(hcd, xhci_pci_quirks);

194 if (retval)

195 return retval;

196

197 xhci = hcd_to_xhci(hcd);

198 if (!usb_hcd_is_primary_hcd(hcd))

199 return 0;

200

201 pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);

202 xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);

203

204 /* Find any debug ports */

205 retval = xhci_pci_reinit(xhci, pdev);

206 if (!retval)

207 return retval;

208

209 kfree(xhci);

210 return retval;

211 }

4819 int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)

4820 {

4821 struct xhci_hcd *xhci;

4822 struct device *dev = hcd->self.controller;

4823 int retval;

4824

4825 /* Accept arbitrarily long scatter-gather lists */

4826 hcd->self.sg_tablesize = ~0;

4827

4828 /* support to build packet from discontinuous buffers */

4829 hcd->self.no_sg_constraint = 1;

4830

4831 /* XHCI controllers don't stop the ep queue on short packets :| */

4832 hcd->self.no_stop_on_short = 1;

4833

4834 if (usb_hcd_is_primary_hcd(hcd)) {

4835 xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);

4836 if (!xhci)

4837 return -ENOMEM;

4838 *((struct xhci_hcd **) hcd->hcd_priv) = xhci;

4839 xhci->main_hcd = hcd;

4840 /* Mark the first roothub as being USB 2.0. 4841 * The xHCI driver will register the USB 3.0 roothub. 4842 */

4843 hcd->speed = HCD_USB2;

4844 hcd->self.root_hub->speed = USB_SPEED_HIGH;

4845 /* 4846 * USB 2.0 roothub under xHCI has an integrated TT, 4847 * (rate matching hub) as opposed to having an OHCI/UHCI 4848 * companion controller. 4849 */

4850 hcd->has_tt = 1;

4851 } else {

4852 /* xHCI private pointer was set in xhci_pci_probe for the second 4853 * registered roothub. 4854 */

4855 return 0;

4856 }

...

xhci_gen_setup最大的作用就是创建了对xHCI至关重要的数据结构——xhci_hcd类型,并完成了大量的初始化工作。今天只关注结构体之间的关系,因此省略了后面初始化的代码。

4835行,创建xhci_hcd类型变量。

4838行,令usb_hcd中的hcd_priv成员指向新创建的xhci_hcd。

4839行,令xhci_hcd中的main_hcd变量指向usb_hcd。

下面回到xhci_pci_probe函数。从此处返回的顺序是xhci_gen_setup->xhci_pci_setup->usb_add_hcd->usb_hcd_pci_probe->xhci_pci_probe。

2.2 xhci_pci_probe调用usb_create_shared_hcd创建第二个usb_hcd结构

现在USB 2.0 roothub设置完成,xhci_pci_probe从240行开始继续执行:

217 static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)

218 {

...

235 retval = usb_hcd_pci_probe(dev, id);

236

237 if (retval)

238 goto put_runtime_pm;

239

240 /* USB 2.0 roothub is stored in the PCI device now. */

241 hcd = dev_get_drvdata(&dev->dev);

242 xhci = hcd_to_xhci(hcd);

243 xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev,

244 pci_name(dev), hcd);

245 if (!xhci->shared_hcd) {

246 retval = -ENOMEM;

247 goto dealloc_usb2_hcd;

248 }

...

243行,这次是xhci_pci_probe亲自调用usb_create_shared_hcd,创建USB 3.x roothub对应的usb_hcd。这次由于有了USB 2.0 roothub对应的usb_hcd作为primary_hcd,因此usb_create_shared_hcd函数在其2445行落入第二个分支,一口气设置好了两个usb_hcd变量的primary_hcd和shared_hcd成员:

2434 struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,

2435 struct device *dev, const char *bus_name,

2436 struct usb_hcd *primary_hcd)

2437 {

2438 struct usb_hcd *hcd;

2439

2440 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);

...

2445 if (primary_hcd == NULL) {

...

2455 } else {

2456 mutex_lock(&usb_port_peer_mutex);

2457 hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;

2458 hcd->primary_hcd = primary_hcd;

2459 primary_hcd->primary_hcd = primary_hcd;

2460 hcd->shared_hcd = primary_hcd;

2461 primary_hcd->shared_hcd = hcd;

2462 mutex_unlock(&usb_port_peer_mutex);

2463 }

顺便,也设置了第二个usb_hcd的driver成员和self.controller,跟前面一样。

再次回到xhci_pci_probe:

217 static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)

218 {

...

240 /* USB 2.0 roothub is stored in the PCI device now. */

241 hcd = dev_get_drvdata(&dev->dev);

242 xhci = hcd_to_xhci(hcd);

243 xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev,

244 pci_name(dev), hcd);

245 if (!xhci->shared_hcd) {

246 retval = -ENOMEM;

247 goto dealloc_usb2_hcd;

248 }

249

250 /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset) 251 * is called by usb_add_hcd(). 252 */

253 *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;

254

255 retval = usb_add_hcd(xhci->shared_hcd, dev->irq,

256 IRQF_SHARED);

257 if (retval)

258 goto put_usb3_hcd;

259 /* Roothub already marked as USB 3.0 speed */

260

261 if (!(xhci->quirks & XHCI_BROKEN_STREAMS) &&

262 HCC_MAX_PSA(xhci->hcc_params) >= 4)

263 xhci->shared_hcd->can_do_streams = 1;

264

265 /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */

266 pm_runtime_put_noidle(&dev->dev);

267

268 return 0;

...

243行,usb_create_shared_hcd返回的时候,直接令xhci_hcd的shared_hcd成员指向新创建的usb_hcd。

253行,令新创建的usb_hcd的hcd_priv成员指向xhci_hcd。

回到最初的示意图,现在所有的连接都已经完成!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux xhci源码,xHCI驱动学习(1) 核心数据结构 的相关文章

随机推荐