首页 > 代码库 > Nouveau源码分析(五):NVIDIA设备初始化之nouveau_drm_load (2)
Nouveau源码分析(五):NVIDIA设备初始化之nouveau_drm_load (2)
Nouveau源码分析(五)
接着上一篇来,先把nouveau_drm_load再贴出一遍来吧:
// /drivers/gpu/drm/nouveau/nouveau_drm.c 364 static int 365 nouveau_drm_load(struct drm_device *dev, unsigned long flags) 366 { 367 struct pci_dev *pdev = dev->pdev; 368 struct nouveau_drm *drm; 369 int ret; 370 371 ret = nouveau_cli_create(nouveau_name(dev), "DRM", sizeof(*drm), 372 (void **)&drm); 373 if (ret) 374 return ret; 375 376 dev->dev_private = drm; 377 drm->dev = dev; 378 nvkm_client(&drm->client.base)->debug = 379 nouveau_dbgopt(nouveau_debug, "DRM"); 380 381 INIT_LIST_HEAD(&drm->clients); 382 spin_lock_init(&drm->tile.lock); 383 384 nouveau_get_hdmi_dev(drm); 385 386 /* make sure AGP controller is in a consistent state before we 387 * (possibly) execute vbios init tables (see nouveau_agp.h) 388 */ 389 if (pdev && drm_pci_device_is_agp(dev) && dev->agp) { 390 const u64 enables = NV_DEVICE_V0_DISABLE_IDENTIFY | 391 NV_DEVICE_V0_DISABLE_MMIO; 392 /* dummy device object, doesn't init anything, but allows 393 * agp code access to registers 394 */ 395 ret = nvif_device_init(&drm->client.base.base, NULL, 396 NVDRM_DEVICE, NV_DEVICE, 397 &(struct nv_device_v0) { 398 .device = ~0, 399 .disable = ~enables, 400 .debug0 = ~0, 401 }, sizeof(struct nv_device_v0), 402 &drm->device); 403 if (ret) 404 goto fail_device; 405 406 nouveau_agp_reset(drm); 407 nvif_device_fini(&drm->device); 408 } 409 410 ret = nvif_device_init(&drm->client.base.base, NULL, NVDRM_DEVICE, 411 NV_DEVICE, 412 &(struct nv_device_v0) { 413 .device = ~0, 414 .disable = 0, 415 .debug0 = 0, 416 }, sizeof(struct nv_device_v0), 417 &drm->device); 418 if (ret) 419 goto fail_device; 420 421 dev->irq_enabled = true; 422 423 /* workaround an odd issue on nvc1 by disabling the device's 424 * nosnoop capability. hopefully won't cause issues until a 425 * better fix is found - assuming there is one... 426 */ 427 if (drm->device.info.chipset == 0xc1) 428 nvif_mask(&drm->device, 0x00088080, 0x00000800, 0x00000000); 429 430 nouveau_vga_init(drm); 431 nouveau_agp_init(drm); 432 433 if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { 434 ret = nouveau_vm_new(nvkm_device(&drm->device), 0, (1ULL << 40), 435 0x1000, &drm->client.vm); 436 if (ret) 437 goto fail_device; 438 439 nvkm_client(&drm->client.base)->vm = drm->client.vm; 440 } 441 442 ret = nouveau_ttm_init(drm); 443 if (ret) 444 goto fail_ttm; 445 446 ret = nouveau_bios_init(dev); 447 if (ret) 448 goto fail_bios; 449 450 ret = nouveau_display_create(dev); 451 if (ret) 452 goto fail_dispctor; 453 454 if (dev->mode_config.num_crtc) { 455 ret = nouveau_display_init(dev); 456 if (ret) 457 goto fail_dispinit; 458 } 459 460 nouveau_sysfs_init(dev); 461 nouveau_hwmon_init(dev); 462 nouveau_accel_init(drm); 463 nouveau_fbcon_init(dev); 464 465 if (nouveau_runtime_pm != 0) { 466 pm_runtime_use_autosuspend(dev->dev); 467 pm_runtime_set_autosuspend_delay(dev->dev, 5000); 468 pm_runtime_set_active(dev->dev); 469 pm_runtime_allow(dev->dev); 470 pm_runtime_mark_last_busy(dev->dev); 471 pm_runtime_put(dev->dev); 472 } 473 return 0; 474 475 fail_dispinit: 476 nouveau_display_destroy(dev); 477 fail_dispctor: 478 nouveau_bios_takedown(dev); 479 fail_bios: 480 nouveau_ttm_fini(drm); 481 fail_ttm: 482 nouveau_agp_fini(drm); 483 nouveau_vga_fini(drm); 484 fail_device: 485 nvif_device_fini(&drm->device); 486 nouveau_cli_destroy(&drm->client); 487 return ret; 488 }
第386行到第408行,注释说的很清楚,重置AGP. 好了来看nvif_device_init:
// /drivers/gpu/drm/nouveau/nvif/device.c 33 int 34 nvif_device_init(struct nvif_object *parent, void (*dtor)(struct nvif_device *), 35 u32 handle, u32 oclass, void *data, u32 size, 36 struct nvif_device *device) 37 { 38 int ret = nvif_object_init(parent, (void *)dtor, handle, oclass, 39 data, size, &device->base); 40 if (ret == 0) { 41 device->object = &device->base; 42 device->info.version = 0; 43 ret = nvif_object_mthd(&device->base, NV_DEVICE_V0_INFO, 44 &device->info, sizeof(device->info)); 45 } 46 return ret; 47 }首先第38行,nvif_object_init,还记得在上一篇中被忽略掉的那部分代码吗? 这里展开说:
// /drivers/gpu/drm/nouveau/nvif/object.c 217 int 218 nvif_object_init(struct nvif_object *parent, void (*dtor)(struct nvif_object *), 219 u32 handle, u32 oclass, void *data, u32 size, 220 struct nvif_object *object) 221 { 222 struct ctor *ctor; 223 int ret = 0; 224 225 object->parent = NULL; 226 object->object = object; 227 nvif_object_ref(parent, &object->parent); 228 kref_init(&object->refcount); 229 object->handle = handle; 230 object->oclass = oclass; 231 object->data = http://www.mamicode.com/NULL;>第238行,分配一个ctor.第244行,把传进来的数据复制进去,数据就是:"&(struct nv_device_v0) { .device = ~0, .disable = ~enables, .debug0 = ~0, }"
下面的几个字段简单说一下:
ctor->ioctl.version 貌似是用来校验版本的东西,目前只可能是0.
ctor->ioctl.type 表示ioctl的命令.
ctor->new.version 貌似和上面那个version一样.
ctor->new.route 表示这是nvif. 除了nvif另一种是usif,用户空间用的.
ctor->new.token 此处是储存对应的nvif_object.
ctor->new.handle 此处是储存要创建的nouveau_object的handle.
ctor->new.oclass 此处是储存要创建的nouveau_object的oclass.
第254行,正式调用nvif_object_ioctl函数.
// /drivers/gpu/drm/nouveau/nvif/object.c 30 int 31 nvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack) 32 { 33 struct nvif_client *client = nvif_client(object); 34 union { 35 struct nvif_ioctl_v0 v0; 36 } *args = data; 37 38 if (size >= sizeof(*args) && args->v0.version == 0) { 39 args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; 40 args->v0.path_nr = 0; 41 while (args->v0.path_nr < ARRAY_SIZE(args->v0.path)) { 42 args->v0.path[args->v0.path_nr++] = object->handle; 43 if (object->parent == object) 44 break; 45 object = object->parent; 46 } 47 } else 48 return -ENOSYS; 49 50 return client->driver->ioctl(client->base.priv, client->super, data, size, hack); 51 }第38行,首先检查version和大小是否正确
第39行,设置一下owner,这个是表示是对nvif还是usif还是any进行ioctl的. 可以防止用户态程序ioctl的时候ioctl到nvif [只能对usif进行ioctl.].
之后第41行一个whlie循环,逐级object = object->parent,然后把handle储存到path里.
第50行,交给client的driver进行处理.
// /drivers/gpu/drm/nouveau/nouveau_nvif.c 54 static int 55 nvkm_client_ioctl(void *priv, bool super, void *data, u32 size, void **hack) 56 { 57 return nvkm_ioctl(priv, super, data, size, hack); 58 }再交给nvkm_ioctl处理.// /drivers/gpu/drm/nouveau/core/core/ioctl.c 502 int 503 nvkm_ioctl(struct nouveau_client *client, bool supervisor, 504 void *data, u32 size, void **hack) 505 { 506 union { 507 struct nvif_ioctl_v0 v0; 508 } *args = data; 509 int ret; 510 511 client->super = supervisor; 512 nv_ioctl(client, "size %d\n", size); 513 514 if (nvif_unpack(args->v0, 0, 0, true)) { 515 nv_ioctl(client, "vers %d type %02x path %d owner %02x\n", 516 args->v0.version, args->v0.type, args->v0.path_nr, 517 args->v0.owner); 518 ret = nvkm_ioctl_path(client->root, args->v0.type, 519 args->v0.path_nr, args->v0.path, 520 data, size, args->v0.owner, 521 &args->v0.route, &args->v0.token); 522 } 523 524 nv_ioctl(client, "return %d\n", ret); 525 if (hack) { 526 *hack = client->data; 527 client->data = http://www.mamicode.com/NULL;>client->super意义不明,忽略之.然后第514行,看看nvif_unpack:
// /drivers/gpu/drm/nouveau/nvif/unpack.h 9 #define nvif_unpack(d,vl,vh,m) ({ 10 if ((vl) == 0 || ret == -ENOSYS) { 11 int _size = sizeof(d); 12 if (_size <= size && (d).version >= (vl) && 13 (d).version <= (vh)) { 14 data = http://www.mamicode.com/(u8 *)data + _size; 15 size = size - _size; 16 ret = ((m) || !size) ? 0 : -E2BIG; 17 } else { 18 ret = -ENOSYS; 19 } 20 } 21 (ret == 0); 22 })大体上来说就是检查版本是否对应,大小是否正常,然后把data加上header的size,把size减去header的size.
第518行,调用nvkm_ioctl_path.
第526行,把data存到*hack里.
重点看nvkm_ioctl_path:
// /drivers/gpu/drm/nouveau/core/core/ioctl.c 459 static int 460 nvkm_ioctl_path(struct nouveau_handle *parent, u32 type, u32 nr, 461 u32 *path, void *data, u32 size, 462 u8 owner, u8 *route, u64 *token) 463 { 464 struct nouveau_handle *handle = parent; 465 struct nouveau_namedb *namedb; 466 struct nouveau_object *object; 467 int ret; 468 469 while ((object = parent->object), nr--) { 470 nv_ioctl(object, "path 0x%08x\n", path[nr]); 471 if (!nv_iclass(object, NV_PARENT_CLASS)) { 472 nv_debug(object, "cannot have children (path)\n"); 473 return -EINVAL; 474 } 475 476 if (!(namedb = (void *)nv_pclass(object, NV_NAMEDB_CLASS)) || 477 !(handle = nouveau_namedb_get(namedb, path[nr]))) { 478 nv_debug(object, "handle 0x%08x not found\n", path[nr]); 479 return -ENOENT; 480 } 481 nouveau_namedb_put(handle); 482 parent = handle; 483 } 484 485 if (owner != NVIF_IOCTL_V0_OWNER_ANY && 486 owner != handle->route) { 487 nv_ioctl(object, "object route != owner\n"); 488 return -EACCES; 489 } 490 *route = handle->route; 491 *token = handle->token; 492 493 if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { 494 if (nvkm_ioctl_v0[type].version == 0) { 495 ret = nvkm_ioctl_v0[type].func(handle, data, size); 496 } 497 } 498 499 return ret; 500 }参数parent是client->root,它对应的object就是它自己client.第469行的while语句,逐个把path[--nr]取出来,从后往前,也就是从parent到children,在namedb中查找.
这样一来,最后的handle就是path[0]所对应的nouveau_handle. 也就是当初调用nvif_object_init第一个参数nvif_object *object对应的nouveau_object *object对应的nouveau_handle *handle! 对于这个例子就是nouveau_client *client的root字段.
接下来第485行,检查owner和handle->route是否匹配,作用前面提到了.
第490行.把handle->route和handle->token储存到*route和*token中,再调用真正的ioctl函数.
// /drivers/gpu/drm/nouveau/core/core/ioctl.c 439 static struct { 440 int version; 441 int (*func)(struct nouveau_handle *, void *, u32); 442 } 443 nvkm_ioctl_v0[] = { 444 { 0x00, nvkm_ioctl_nop }, 445 { 0x00, nvkm_ioctl_sclass }, 446 { 0x00, nvkm_ioctl_new }, 447 { 0x00, nvkm_ioctl_del }, 448 { 0x00, nvkm_ioctl_mthd }, 449 { 0x00, nvkm_ioctl_rd }, 450 { 0x00, nvkm_ioctl_wr }, 451 { 0x00, nvkm_ioctl_map }, 452 { 0x00, nvkm_ioctl_unmap }, 453 { 0x00, nvkm_ioctl_ntfy_new }, 454 { 0x00, nvkm_ioctl_ntfy_del }, 455 { 0x00, nvkm_ioctl_ntfy_get }, 456 { 0x00, nvkm_ioctl_ntfy_put }, 457 };本例当然是nvkm_ioctl_new,来看一下这个函数:87 static int 88 nvkm_ioctl_new(struct nouveau_handle *parent, void *data, u32 size) 89 { 90 union { 91 struct nvif_ioctl_new_v0 v0; 92 } *args = data; 93 struct nouveau_client *client = nouveau_client(parent->object); 94 struct nouveau_object *engctx = NULL; 95 struct nouveau_object *object = NULL; 96 struct nouveau_object *engine; 97 struct nouveau_oclass *oclass; 98 struct nouveau_handle *handle; 99 u32 _handle, _oclass; 100 int ret; 101 102 nv_ioctl(client, "new size %d\n", size); 103 if (nvif_unpack(args->v0, 0, 0, true)) { 104 _handle = args->v0.handle; 105 _oclass = args->v0.oclass; 106 } else 107 return ret; 108 109 nv_ioctl(client, "new vers %d handle %08x class %08x " 110 "route %02x token %llx\n", 111 args->v0.version, _handle, _oclass, 112 args->v0.route, args->v0.token); 113 114 if (!nv_iclass(parent->object, NV_PARENT_CLASS)) { 115 nv_debug(parent->object, "cannot have children (ctor)\n"); 116 ret = -ENODEV; 117 goto fail_class; 118 } 119 120 /* check that parent supports the requested subclass */ 121 ret = nouveau_parent_sclass(parent->object, _oclass, &engine, &oclass); 122 if (ret) { 123 nv_debug(parent->object, "illegal class 0x%04x\n", _oclass); 124 goto fail_class; 125 } 126 127 /* make sure engine init has been completed *before* any objects 128 * it controls are created - the constructors may depend on 129 * state calculated at init (ie. default context construction) 130 */ 131 if (engine) { 132 ret = nouveau_object_inc(engine); 133 if (ret) 134 goto fail_class; 135 } 136 137 /* if engine requires it, create a context object to insert 138 * between the parent and its children (eg. PGRAPH context) 139 */ 140 if (engine && nv_engine(engine)->cclass) { 141 ret = nouveau_object_ctor(parent->object, engine, 142 nv_engine(engine)->cclass, 143 data, size, &engctx); 144 if (ret) 145 goto fail_engctx; 146 } else { 147 nouveau_object_ref(parent->object, &engctx); 148 } 149 150 /* finally, create new object and bind it to its handle */ 151 ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object); 152 client->data = http://www.mamicode.com/object;>第103行,再次使用nvif_unpack检查参数的正确性.第121行,这就是u32 oclass到nouveau_oclass *oclass的转换了.
// /drivers/gpu/drm/nouveau/core/core/parent.c 29 int 30 nouveau_parent_sclass(struct nouveau_object *parent, u16 handle, 31 struct nouveau_object **pengine, 32 struct nouveau_oclass **poclass) 33 { 34 struct nouveau_sclass *sclass; 35 struct nouveau_engine *engine; 36 struct nouveau_oclass *oclass; 37 u64 mask; 38 39 sclass = nv_parent(parent)->sclass; 40 while (sclass) { 41 if ((sclass->oclass->handle & 0xffff) == handle) { 42 *pengine = parent->engine; 43 *poclass = sclass->oclass; 44 return 0; 45 } 46 47 sclass = sclass->sclass; 48 } 49 50 mask = nv_parent(parent)->engine; 51 while (mask) { 52 int i = __ffs64(mask); 53 54 if (nv_iclass(parent, NV_CLIENT_CLASS)) 55 engine = nv_engine(nv_client(parent)->device); 56 else 57 engine = nouveau_engine(parent, i); 58 59 if (engine) { 60 oclass = engine->sclass; 61 while (oclass->ofuncs) { 62 if ((oclass->handle & 0xffff) == handle) { 63 *pengine = nv_object(engine); 64 *poclass = oclass; 65 return 0; 66 } 67 oclass++; 68 } 69 } 70 71 mask &= ~(1ULL << i); 72 } 73 74 return -EINVAL; 75 }首先第40行的while语句,直接在parent->sclass中寻找,这个东西可以看作是一个单向链表. 对于这个例子,parent->sclass为0,故不存在这种可能性.然后第52行,从((nouveau_parent *)parent)->engine指示的subdev中寻找. __ffs64返回的是二进制中第一个为1的位是第几位. 比如0b1返回0 ,0b1000返回3.
如果parent不是一个nouveau_client对象,那么就先向上获取一个nouveau_device对象,然后返回device->subdev[i] .
如果parent是一个nouveau_client对象,那么直接使用client->device.
对于本例当然是后者,也就是直接使用我们在第三篇中用nouveau_device_create创建的那个nouveau_device *device. (此处需要注意nouveau_parent::sclass是个单向链表,但nouveau_device::sclass是个数组.)
还记得当初传递进去的sclass是什么吗?
// /drivers/gpu/drm/nouveau/core/engine/device/base.c 501 static struct nouveau_oclass 502 nouveau_device_sclass[] = { 503 { 0x0080, &nouveau_devobj_ofuncs }, 504 {} 505 };看看我们现在的这个oclass是什么:// /drivers/gpu/drm/nouveau/core/include/nvif/class.h 9 #define NV_DEVICE 0x00000080匹配成功! 于是对pengine和poclass进行赋值操作,返回.
第132行,对engine执行nouveau_object_inc函数:
// /drivers/gpu/drm/nouveau/core/core/object.c 164 int 165 nouveau_object_inc(struct nouveau_object *object) 166 { 167 int ref = atomic_add_return(1, &object->usecount); 168 int ret; 169 170 nv_trace(object, "use(+1) == %d\n", atomic_read(&object->usecount)); 171 if (ref != 1) 172 return 0; 173 174 nv_trace(object, "initialising...\n"); 175 if (object->parent) { 176 ret = nouveau_object_inc(object->parent); 177 if (ret) { 178 nv_error(object, "parent failed, %d\n", ret); 179 goto fail_parent; 180 } 181 } 182 183 if (object->engine) { 184 mutex_lock(&nv_subdev(object->engine)->mutex); 185 ret = nouveau_object_inc(object->engine); 186 mutex_unlock(&nv_subdev(object->engine)->mutex); 187 if (ret) { 188 nv_error(object, "engine failed, %d\n", ret); 189 goto fail_engine; 190 } 191 } 192 193 ret = nv_ofuncs(object)->init(object); 194 atomic_set(&object->usecount, 1); 195 if (ret) { 196 nv_error(object, "init failed, %d\n", ret); 197 goto fail_self; 198 } 199 200 nv_trace(object, "initialised\n"); 201 return 0; 202 203 fail_self: 204 if (object->engine) { 205 mutex_lock(&nv_subdev(object->engine)->mutex); 206 nouveau_object_dec(object->engine, false); 207 mutex_unlock(&nv_subdev(object->engine)->mutex); 208 } 209 fail_engine: 210 if (object->parent) 211 nouveau_object_dec(object->parent, false); 212 fail_parent: 213 atomic_dec(&object->usecount); 214 return ret; 215 }因此这是第一次执行nouveau_object_inc函数,所以第171行并没有返回.然后第176行,对parent执行inc操作,第185行,对engine执行inc操作. 此处对由于parent和engine都为0,所以并没有执行进这两个if语句.
第193行,执行oclass里的init函数:
// /drivers/gpu/drm/nouveau/core/engine/device/base.c 652 static struct nouveau_oclass 653 nouveau_device_oclass = { 654 .handle = NV_ENGINE(DEVICE, 0x00), 655 .ofuncs = &(struct nouveau_ofuncs) { 656 .dtor = nouveau_device_dtor, 657 .init = nouveau_device_init, 658 .fini = nouveau_device_fini, 659 }, 660 };// /drivers/gpu/drm/nouveau/core/engine/device/base.c 557 static int 558 nouveau_device_init(struct nouveau_object *object) 559 { 560 struct nouveau_device *device = (void *)object; 561 struct nouveau_object *subdev; 562 int ret, i = 0; 563 564 ret = nvkm_acpi_init(device); 565 if (ret) 566 goto fail; 567 568 for (i = 0; i < NVDEV_SUBDEV_NR; i++) { 569 if ((subdev = device->subdev[i])) { 570 if (!nv_iclass(subdev, NV_ENGINE_CLASS)) { 571 ret = nouveau_object_inc(subdev); 572 if (ret) 573 goto fail; 574 } else { 575 nouveau_subdev_reset(subdev); 576 } 577 } 578 } 579 580 ret = 0; 581 fail: 582 for (--i; ret && i >= 0; i--) { 583 if ((subdev = device->subdev[i])) { 584 if (!nv_iclass(subdev, NV_ENGINE_CLASS)) 585 nouveau_object_dec(subdev, false); 586 } 587 } 588 589 if (ret) 590 nvkm_acpi_fini(device, false); 591 return ret; 592 }首先第564行执行nvkm_acpi_init,这个函数对ACPI模块注册了一个回调函数,当AC Adapter发出事件时就触发device->event,然后这个事件会在clock中被受理. 暂时不展开说了.接下来第568行的for语句,遍历一下device->subdev,执行nouveau_object_inc或者nouveau_subdev_reset.
然后第582行的for语句,再遍历一遍执行nouveau_object_dec. 前面执行一次inc,这里再执行一次dec,也就相当于reset了. 所以这段代码的作用就是reset所有subdev..
但现在subdev还没有被初始化,因此所有if语句都不会进入,所以啥也没做直接返回.
回到nvkm_ioctl_new,第141行,检查engine有没有cclass,有的话创建一个context object,对于这个例子没有.
第151行,创建一个nvif_object对应的nouveau_object:
// /drivers/gpu/drm/nouveau/core/core/object.c 105 int 106 nouveau_object_ctor(struct nouveau_object *parent, 107 struct nouveau_object *engine, 108 struct nouveau_oclass *oclass, void *data, u32 size, 109 struct nouveau_object **pobject) 110 { 111 struct nouveau_ofuncs *ofuncs = oclass->ofuncs; 112 struct nouveau_object *object = NULL; 113 int ret; 114 115 ret = ofuncs->ctor(parent, engine, oclass, data, size, &object); 116 *pobject = object; 117 if (ret < 0) { 118 if (ret != -ENODEV) { 119 nv_error(parent, "failed to create 0x%08x, %d\n", 120 oclass->handle, ret); 121 } 122 123 if (object) { 124 ofuncs->dtor(object); 125 *pobject = NULL; 126 } 127 128 return ret; 129 } 130 131 if (ret == 0) { 132 nv_trace(object, "created\n"); 133 atomic_set(&object->refcount, 1); 134 } 135 136 return 0; 137 }这个函数就是调用了一下oclass里的ctor函数,然后进行错误检查,出错就销毁一下. 第133行,再初始化一下refcount,返回.对于这个例子,对应的是"nouveau_devobj_init",这又是一个超级长的函数,所以决定下一篇单独描述这个函数,现在只需要知道它对设备的类型进行了检测,然后初始化了各个subdev就可以了.
再看nvkm_ioctl_new的第156行,对新创建的nouveau_object执行nouveau_object_inc操作,将会调用它的oclass里对应的init函数,对于这个例子是个空函数,无视.
第160行,创建一个nouveau_handle,它将会被自动加入到对应的namedb中,下一次我们就能从namedb中找到这个nouveau_object了.
第165行,执行一个nouveau_handle_init函数,这个函数首先执行nouveau_object_inc(handle->object); 然后对handle->tree链表里储存的所有children进行nouveau_handle_init操作.
然后第166行,把route和token储存到handle里.
最后第171行到第181行,进行一些清理操作,返回. [这些清理操作绝对不会执行任何对象的析构函数,或者导致内存释放. 因为这些对象均已经在其他地方被引用. (除非是出错之后用goto跳过来的,那种情况另说.)]
于是我们一路回到了nvif_device_init, 第43行,发送一个mthd ioctl,作用是获取设备信息储存到device->info里,这个同样留到下一篇在说.
然后我们回到了nouveau_drm_load的第395行,这一篇就先写到这里了.
Nouveau源码分析(五):NVIDIA设备初始化之nouveau_drm_load (2)