xf86pciBus.c revision f7df2e56
1/* 2 * Copyright (c) 1997-2003 by The XFree86 Project, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Except as contained in this notice, the name of the copyright holder(s) 23 * and author(s) shall not be used in advertising or otherwise to promote 24 * the sale, use or other dealings in this Software without prior written 25 * authorization from the copyright holder(s) and author(s). 26 */ 27 28/* 29 * This file contains the interfaces to the bus-specific code 30 */ 31#ifdef HAVE_XORG_CONFIG_H 32#include <xorg-config.h> 33#endif 34 35#include <ctype.h> 36#include <stdlib.h> 37#include <unistd.h> 38#include <X11/X.h> 39#include <pciaccess.h> 40#include "os.h" 41#include "Pci.h" 42#include "xf86.h" 43#include "xf86Priv.h" 44#include "dirent.h" /* DIR, FILE type definitions */ 45 46/* Bus-specific headers */ 47#include "xf86Bus.h" 48 49#define XF86_OS_PRIVS 50#include "xf86_OSproc.h" 51 52#define PCI_VENDOR_GENERIC 0x00FF 53 54/* Bus-specific globals */ 55int pciSlotClaimed = 0; 56 57#define PCIINFOCLASSES(c) \ 58 ( (((c) & 0x00ff0000) == (PCI_CLASS_PREHISTORIC << 16)) \ 59 || (((c) & 0x00ff0000) == (PCI_CLASS_DISPLAY << 16)) \ 60 || ((((c) & 0x00ffff00) \ 61 == ((PCI_CLASS_MULTIMEDIA << 16) | (PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8)))) \ 62 || ((((c) & 0x00ffff00) \ 63 == ((PCI_CLASS_PROCESSOR << 16) | (PCI_SUBCLASS_PROCESSOR_COPROC << 8)))) ) 64 65/* 66 * PCI classes that have messages printed always. The others are only 67 * have a message printed when the vendor/dev IDs are recognised. 68 */ 69#define PCIALWAYSPRINTCLASSES(c) \ 70 ( (((c) & 0x00ffff00) \ 71 == ((PCI_CLASS_PREHISTORIC << 16) | (PCI_SUBCLASS_PREHISTORIC_VGA << 8))) \ 72 || (((c) & 0x00ff0000) == (PCI_CLASS_DISPLAY << 16)) \ 73 || ((((c) & 0x00ffff00) \ 74 == ((PCI_CLASS_MULTIMEDIA << 16) | (PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8)))) ) 75 76#define IS_VGA(c) \ 77 (((c) & 0x00ffff00) \ 78 == ((PCI_CLASS_DISPLAY << 16) | (PCI_SUBCLASS_DISPLAY_VGA << 8))) 79 80static struct pci_slot_match xf86IsolateDevice = { 81 PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0 82}; 83 84/* 85 * xf86Bus.c interface 86 */ 87 88void 89xf86PciProbe(void) 90{ 91 int i = 0, k; 92 int num = 0; 93 struct pci_device *info; 94 struct pci_device_iterator *iter; 95 struct pci_device **xf86PciVideoInfo = NULL; 96 97 if (!xf86scanpci()) { 98 xf86PciVideoInfo = NULL; 99 return; 100 } 101 102 iter = pci_slot_match_iterator_create(&xf86IsolateDevice); 103 while ((info = pci_device_next(iter)) != NULL) { 104 if (PCIINFOCLASSES(info->device_class)) { 105 num++; 106 xf86PciVideoInfo = xnfreallocarray(xf86PciVideoInfo, 107 num + 1, 108 sizeof(struct pci_device *)); 109 xf86PciVideoInfo[num] = NULL; 110 xf86PciVideoInfo[num - 1] = info; 111 112 pci_device_probe(info); 113 if (primaryBus.type == BUS_NONE && pci_device_is_boot_vga(info)) { 114 primaryBus.type = BUS_PCI; 115 primaryBus.id.pci = info; 116 } 117 info->user_data = 0; 118 } 119 } 120 free(iter); 121 122 /* If we haven't found a primary device try a different heuristic */ 123 if (primaryBus.type == BUS_NONE && num) { 124 for (i = 0; i < num; i++) { 125 uint16_t command; 126 127 info = xf86PciVideoInfo[i]; 128 pci_device_cfg_read_u16(info, &command, 4); 129 130 if ((command & PCI_CMD_MEM_ENABLE) 131 && ((num == 1) || IS_VGA(info->device_class))) { 132 if (primaryBus.type == BUS_NONE) { 133 primaryBus.type = BUS_PCI; 134 primaryBus.id.pci = info; 135 } 136 else { 137 xf86Msg(X_NOTICE, 138 "More than one possible primary device found\n"); 139 primaryBus.type ^= (BusType) (-1); 140 } 141 } 142 } 143 } 144 145 /* Print a summary of the video devices found */ 146 for (k = 0; k < num; k++) { 147 const char *prim = " "; 148 Bool memdone = FALSE, iodone = FALSE; 149 150 info = xf86PciVideoInfo[k]; 151 152 if (!PCIALWAYSPRINTCLASSES(info->device_class)) 153 continue; 154 155 if (xf86IsPrimaryPci(info)) 156 prim = "*"; 157 158 xf86Msg(X_PROBED, "PCI:%s(%u:%u:%u:%u) %04x:%04x:%04x:%04x ", prim, 159 info->domain, info->bus, info->dev, info->func, 160 info->vendor_id, info->device_id, 161 info->subvendor_id, info->subdevice_id); 162 163 xf86ErrorF("rev %d", info->revision); 164 165 for (i = 0; i < 6; i++) { 166 struct pci_mem_region *r = &info->regions[i]; 167 168 if (r->size && !r->is_IO) { 169 if (!memdone) { 170 xf86ErrorF(", Mem @ "); 171 memdone = TRUE; 172 } 173 else 174 xf86ErrorF(", "); 175 xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size); 176 } 177 } 178 179 for (i = 0; i < 6; i++) { 180 struct pci_mem_region *r = &info->regions[i]; 181 182 if (r->size && r->is_IO) { 183 if (!iodone) { 184 xf86ErrorF(", I/O @ "); 185 iodone = TRUE; 186 } 187 else 188 xf86ErrorF(", "); 189 xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size); 190 } 191 } 192 193 if (info->rom_size) { 194 xf86ErrorF(", BIOS @ 0x\?\?\?\?\?\?\?\?/%ld", 195 (long) info->rom_size); 196 } 197 198 xf86ErrorF("\n"); 199 } 200 free(xf86PciVideoInfo); 201} 202 203/* 204 * If the slot requested is already in use, return -1. 205 * Otherwise, claim the slot for the screen requesting it. 206 */ 207 208int 209xf86ClaimPciSlot(struct pci_device *d, DriverPtr drvp, 210 int chipset, GDevPtr dev, Bool active) 211{ 212 EntityPtr p = NULL; 213 int num; 214 215 if (xf86CheckPciSlot(d)) { 216 num = xf86AllocateEntity(); 217 p = xf86Entities[num]; 218 p->driver = drvp; 219 p->chipset = chipset; 220 p->bus.type = BUS_PCI; 221 p->bus.id.pci = d; 222 p->active = active; 223 p->inUse = FALSE; 224 if (dev) 225 xf86AddDevToEntity(num, dev); 226 pciSlotClaimed++; 227 228 return num; 229 } 230 else 231 return -1; 232} 233 234/* 235 * Unclaim PCI slot, e.g. if probing failed, so that a different driver can claim. 236 */ 237void 238xf86UnclaimPciSlot(struct pci_device *d, GDevPtr dev) 239{ 240 int i; 241 242 for (i = 0; i < xf86NumEntities; i++) { 243 const EntityPtr p = xf86Entities[i]; 244 245 if ((p->bus.type == BUS_PCI) && (p->bus.id.pci == d)) { 246 /* Probably the slot should be deallocated? */ 247 xf86RemoveDevFromEntity(i, dev); 248 pciSlotClaimed--; 249 p->bus.type = BUS_NONE; 250 return; 251 } 252 } 253} 254 255/* 256 * Parse a BUS ID string, and return the PCI bus parameters if it was 257 * in the correct format for a PCI bus id. 258 */ 259 260Bool 261xf86ParsePciBusString(const char *busID, int *bus, int *device, int *func) 262{ 263 /* 264 * The format is assumed to be "bus[@domain]:device[:func]", where domain, 265 * bus, device and func are decimal integers. domain and func may be 266 * omitted and assumed to be zero, although doing this isn't encouraged. 267 */ 268 269 char *p, *s, *d; 270 const char *id; 271 int i; 272 273 if (StringToBusType(busID, &id) != BUS_PCI) 274 return FALSE; 275 276 s = xstrdup(id); 277 p = strtok(s, ":"); 278 if (p == NULL || *p == 0) { 279 free(s); 280 return FALSE; 281 } 282 d = strpbrk(p, "@"); 283 if (d != NULL) { 284 *(d++) = 0; 285 for (i = 0; d[i] != 0; i++) { 286 if (!isdigit(d[i])) { 287 free(s); 288 return FALSE; 289 } 290 } 291 } 292 for (i = 0; p[i] != 0; i++) { 293 if (!isdigit(p[i])) { 294 free(s); 295 return FALSE; 296 } 297 } 298 *bus = atoi(p); 299 if (d != NULL && *d != 0) 300 *bus += atoi(d) << 8; 301 p = strtok(NULL, ":"); 302 if (p == NULL || *p == 0) { 303 free(s); 304 return FALSE; 305 } 306 for (i = 0; p[i] != 0; i++) { 307 if (!isdigit(p[i])) { 308 free(s); 309 return FALSE; 310 } 311 } 312 *device = atoi(p); 313 *func = 0; 314 p = strtok(NULL, ":"); 315 if (p == NULL || *p == 0) { 316 free(s); 317 return TRUE; 318 } 319 for (i = 0; p[i] != 0; i++) { 320 if (!isdigit(p[i])) { 321 free(s); 322 return FALSE; 323 } 324 } 325 *func = atoi(p); 326 free(s); 327 return TRUE; 328} 329 330/* 331 * Compare a BUS ID string with a PCI bus id. Return TRUE if they match. 332 */ 333 334Bool 335xf86ComparePciBusString(const char *busID, int bus, int device, int func) 336{ 337 int ibus, idevice, ifunc; 338 339 if (xf86ParsePciBusString(busID, &ibus, &idevice, &ifunc)) { 340 return bus == ibus && device == idevice && func == ifunc; 341 } 342 else { 343 return FALSE; 344 } 345} 346 347/* 348 * xf86IsPrimaryPci() -- return TRUE if primary device 349 * is PCI and bus, dev and func numbers match. 350 */ 351 352Bool 353xf86IsPrimaryPci(struct pci_device *pPci) 354{ 355 if (primaryBus.type == BUS_PCI) 356 return pPci == primaryBus.id.pci; 357#ifdef XSERVER_PLATFORM_BUS 358 if (primaryBus.type == BUS_PLATFORM) 359 if (primaryBus.id.plat->pdev) 360 if (MATCH_PCI_DEVICES(primaryBus.id.plat->pdev, pPci)) 361 return TRUE; 362#endif 363 return FALSE; 364} 365 366/* 367 * xf86GetPciInfoForEntity() -- Get the pciVideoRec of entity. 368 */ 369struct pci_device * 370xf86GetPciInfoForEntity(int entityIndex) 371{ 372 EntityPtr p; 373 374 if (entityIndex >= xf86NumEntities) 375 return NULL; 376 377 p = xf86Entities[entityIndex]; 378 switch (p->bus.type) { 379 case BUS_PCI: 380 return p->bus.id.pci; 381 case BUS_PLATFORM: 382 return p->bus.id.plat->pdev; 383 default: 384 break; 385 } 386 return NULL; 387} 388 389/* 390 * xf86CheckPciMemBase() checks that the memory base value matches one of the 391 * PCI base address register values for the given PCI device. 392 */ 393Bool 394xf86CheckPciMemBase(struct pci_device *pPci, memType base) 395{ 396 int i; 397 398 for (i = 0; i < 6; i++) 399 if (base == pPci->regions[i].base_addr) 400 return TRUE; 401 return FALSE; 402} 403 404/* 405 * Check if the slot requested is free. If it is already in use, return FALSE. 406 */ 407 408Bool 409xf86CheckPciSlot(const struct pci_device *d) 410{ 411 int i; 412 413 for (i = 0; i < xf86NumEntities; i++) { 414 const EntityPtr p = xf86Entities[i]; 415 416 if ((p->bus.type == BUS_PCI) && (p->bus.id.pci == d)) { 417 return FALSE; 418 } 419#ifdef XSERVER_PLATFORM_BUS 420 if ((p->bus.type == BUS_PLATFORM) && (p->bus.id.plat->pdev)) { 421 struct pci_device *ud = p->bus.id.plat->pdev; 422 if (MATCH_PCI_DEVICES(ud, d)) 423 return FALSE; 424 } 425#endif 426 } 427 return TRUE; 428} 429 430#define END_OF_MATCHES(m) \ 431 (((m).vendor_id == 0) && ((m).device_id == 0) && ((m).subvendor_id == 0)) 432 433Bool 434xf86PciAddMatchingDev(DriverPtr drvp) 435{ 436 const struct pci_id_match *const devices = drvp->supported_devices; 437 int j; 438 struct pci_device *pPci; 439 struct pci_device_iterator *iter; 440 int numFound = 0; 441 442 iter = pci_id_match_iterator_create(NULL); 443 while ((pPci = pci_device_next(iter)) != NULL) { 444 /* Determine if this device is supported by the driver. If it is, 445 * add it to the list of devices to configure. 446 */ 447 for (j = 0; !END_OF_MATCHES(devices[j]); j++) { 448 if (PCI_ID_COMPARE(devices[j].vendor_id, pPci->vendor_id) 449 && PCI_ID_COMPARE(devices[j].device_id, pPci->device_id) 450 && ((devices[j].device_class_mask & pPci->device_class) 451 == devices[j].device_class)) { 452 if (xf86CheckPciSlot(pPci)) { 453 GDevPtr pGDev = 454 xf86AddBusDeviceToConfigure(drvp->driverName, BUS_PCI, 455 pPci, -1); 456 if (pGDev != NULL) { 457 /* After configure pass 1, chipID and chipRev are 458 * treated as over-rides, so clobber them here. 459 */ 460 pGDev->chipID = -1; 461 pGDev->chipRev = -1; 462 } 463 464 numFound++; 465 } 466 467 break; 468 } 469 } 470 } 471 472 pci_iterator_destroy(iter); 473 474 return numFound != 0; 475} 476 477Bool 478xf86PciProbeDev(DriverPtr drvp) 479{ 480 int i, j; 481 struct pci_device *pPci; 482 Bool foundScreen = FALSE; 483 const struct pci_id_match *const devices = drvp->supported_devices; 484 GDevPtr *devList; 485 const unsigned numDevs = xf86MatchDevice(drvp->driverName, &devList); 486 487 for (i = 0; i < numDevs; i++) { 488 struct pci_device_iterator *iter; 489 unsigned device_id; 490 491 /* Find the pciVideoRec associated with this device section. 492 */ 493 iter = pci_id_match_iterator_create(NULL); 494 while ((pPci = pci_device_next(iter)) != NULL) { 495 if (devList[i]->busID && *devList[i]->busID) { 496 if (xf86ComparePciBusString(devList[i]->busID, 497 ((pPci->domain << 8) 498 | pPci->bus), 499 pPci->dev, pPci->func)) { 500 break; 501 } 502 } 503 else if (xf86IsPrimaryPci(pPci)) { 504 break; 505 } 506 } 507 508 pci_iterator_destroy(iter); 509 510 if (pPci == NULL) { 511 continue; 512 } 513 device_id = (devList[i]->chipID > 0) 514 ? devList[i]->chipID : pPci->device_id; 515 516 /* Once the pciVideoRec is found, determine if the device is supported 517 * by the driver. If it is, probe it! 518 */ 519 for (j = 0; !END_OF_MATCHES(devices[j]); j++) { 520 if (PCI_ID_COMPARE(devices[j].vendor_id, pPci->vendor_id) 521 && PCI_ID_COMPARE(devices[j].device_id, device_id) 522 && ((devices[j].device_class_mask & pPci->device_class) 523 == devices[j].device_class)) { 524 int entry; 525 526 /* Allow the same entity to be used more than once for 527 * devices with multiple screens per entity. This assumes 528 * implicitly that there will be a screen == 0 instance. 529 * 530 * FIXME Need to make sure that two different drivers don't 531 * FIXME claim the same screen > 0 instance. 532 */ 533 if ((devList[i]->screen == 0) && !xf86CheckPciSlot(pPci)) 534 continue; 535 536 DebugF("%s: card at %d:%d:%d is claimed by a Device section\n", 537 drvp->driverName, pPci->bus, pPci->dev, pPci->func); 538 539 /* Allocate an entry in the lists to be returned */ 540 entry = xf86ClaimPciSlot(pPci, drvp, device_id, 541 devList[i], devList[i]->active); 542 543 if ((entry == -1) && (devList[i]->screen > 0)) { 544 unsigned k; 545 546 for (k = 0; k < xf86NumEntities; k++) { 547 EntityPtr pEnt = xf86Entities[k]; 548 549 if (pEnt->bus.type != BUS_PCI) 550 continue; 551 if (pEnt->bus.id.pci == pPci) { 552 entry = k; 553 xf86AddDevToEntity(k, devList[i]); 554 break; 555 } 556 } 557 } 558 559 if (entry != -1) { 560 if ((*drvp->PciProbe) (drvp, entry, pPci, 561 devices[j].match_data)) { 562 foundScreen = TRUE; 563 } 564 else 565 xf86UnclaimPciSlot(pPci, devList[i]); 566 } 567 568 break; 569 } 570 } 571 } 572 free(devList); 573 574 return foundScreen; 575} 576 577void 578xf86PciIsolateDevice(const char *argument) 579{ 580 int bus, device, func; 581 582 if (sscanf(argument, "PCI:%d:%d:%d", &bus, &device, &func) == 3) { 583 xf86IsolateDevice.domain = PCI_DOM_FROM_BUS(bus); 584 xf86IsolateDevice.bus = PCI_BUS_NO_DOMAIN(bus); 585 xf86IsolateDevice.dev = device; 586 xf86IsolateDevice.func = func; 587 } 588 else 589 FatalError("Invalid isolated device specification\n"); 590} 591 592static Bool 593pciDeviceHasBars(struct pci_device *pci) 594{ 595 int i; 596 597 for (i = 0; i < 6; i++) 598 if (pci->regions[i].size) 599 return TRUE; 600 601 if (pci->rom_size) 602 return TRUE; 603 604 return FALSE; 605} 606 607struct Inst { 608 struct pci_device *pci; 609 GDevPtr dev; 610 Bool foundHW; /* PCIid in list of supported chipsets */ 611 Bool claimed; /* BusID matches with a device section */ 612 int chip; 613 int screen; 614}; 615 616/** 617 * Find set of unclaimed devices matching a given vendor ID. 618 * 619 * Used by drivers to find as yet unclaimed devices matching the specified 620 * vendor ID. 621 * 622 * \param driverName Name of the driver. This is used to find Device 623 * sections in the config file. 624 * \param vendorID PCI vendor ID of associated devices. If zero, then 625 * the true vendor ID must be encoded in the \c PCIid 626 * fields of the \c PCIchipsets entries. 627 * \param chipsets Symbol table used to associate chipset names with 628 * PCI IDs. 629 * \param devList List of Device sections parsed from the config file. 630 * \param numDevs Number of entries in \c devList. 631 * \param drvp Pointer the driver's control structure. 632 * \param foundEntities Returned list of entity indicies associated with the 633 * driver. 634 * 635 * \returns 636 * The number of elements in returned in \c foundEntities on success or zero 637 * on failure. 638 * 639 * \todo 640 * This function does a bit more than short description says. Fill in some 641 * more of the details of its operation. 642 * 643 * \todo 644 * The \c driverName parameter is redundant. It is the same as 645 * \c DriverRec::driverName. In a future version of this function, remove 646 * that parameter. 647 */ 648int 649xf86MatchPciInstances(const char *driverName, int vendorID, 650 SymTabPtr chipsets, PciChipsets * PCIchipsets, 651 GDevPtr * devList, int numDevs, DriverPtr drvp, 652 int **foundEntities) 653{ 654 int i, j; 655 struct pci_device *pPci; 656 struct pci_device_iterator *iter; 657 struct Inst *instances = NULL; 658 int numClaimedInstances = 0; 659 int allocatedInstances = 0; 660 int numFound = 0; 661 SymTabRec *c; 662 PciChipsets *id; 663 int *retEntities = NULL; 664 665 *foundEntities = NULL; 666 667 /* Each PCI device will contribute at least one entry. Each device 668 * section can contribute at most one entry. The sum of the two is 669 * guaranteed to be larger than the maximum possible number of entries. 670 * Do this calculation and memory allocation once now to eliminate the 671 * need for realloc calls inside the loop. 672 */ 673 if (!(xf86DoConfigure && xf86DoConfigurePass1)) { 674 unsigned max_entries = numDevs; 675 676 iter = pci_slot_match_iterator_create(NULL); 677 while ((pPci = pci_device_next(iter)) != NULL) { 678 max_entries++; 679 } 680 681 pci_iterator_destroy(iter); 682 instances = xnfallocarray(max_entries, sizeof(struct Inst)); 683 } 684 685 iter = pci_slot_match_iterator_create(NULL); 686 while ((pPci = pci_device_next(iter)) != NULL) { 687 unsigned device_class = pPci->device_class; 688 Bool foundVendor = FALSE; 689 690 /* Convert the pre-PCI 2.0 device class for a VGA adapter to the 691 * 2.0 version of the same class. 692 */ 693 if (device_class == 0x00000101) { 694 device_class = 0x00030000; 695 } 696 697 /* Find PCI devices that match the given vendor ID. The vendor ID is 698 * either specified explicitly as a parameter to the function or 699 * implicitly encoded in the high bits of id->PCIid. 700 * 701 * The first device with a matching vendor is recorded, even if the 702 * device ID doesn't match. This is done because the Device section 703 * in the xorg.conf file can over-ride the device ID. A matching PCI 704 * ID might not be found now, but after the device ID over-ride is 705 * applied there /might/ be a match. 706 */ 707 for (id = PCIchipsets; id->PCIid != -1; id++) { 708 const unsigned vendor_id = ((id->PCIid & 0xFFFF0000) >> 16) 709 | vendorID; 710 const unsigned device_id = (id->PCIid & 0x0000FFFF); 711 const unsigned match_class = 0x00030000 | id->PCIid; 712 713 if ((vendor_id == pPci->vendor_id) 714 || ((vendorID == PCI_VENDOR_GENERIC) && 715 (match_class == device_class))) { 716 if (!foundVendor && (instances != NULL)) { 717 ++allocatedInstances; 718 instances[allocatedInstances - 1].pci = pPci; 719 instances[allocatedInstances - 1].dev = NULL; 720 instances[allocatedInstances - 1].claimed = FALSE; 721 instances[allocatedInstances - 1].foundHW = FALSE; 722 instances[allocatedInstances - 1].screen = 0; 723 } 724 725 foundVendor = TRUE; 726 727 if ((device_id == pPci->device_id) 728 || ((vendorID == PCI_VENDOR_GENERIC) 729 && (match_class == device_class))) { 730 if (instances != NULL) { 731 instances[allocatedInstances - 1].foundHW = TRUE; 732 instances[allocatedInstances - 1].chip = id->numChipset; 733 } 734 735 if (xf86DoConfigure && xf86DoConfigurePass1) { 736 if (xf86CheckPciSlot(pPci)) { 737 GDevPtr pGDev = 738 xf86AddBusDeviceToConfigure(drvp->driverName, 739 BUS_PCI, pPci, -1); 740 741 if (pGDev) { 742 /* After configure pass 1, chipID and chipRev 743 * are treated as over-rides, so clobber them 744 * here. 745 */ 746 pGDev->chipID = -1; 747 pGDev->chipRev = -1; 748 } 749 750 numFound++; 751 } 752 } 753 else { 754 numFound++; 755 } 756 757 break; 758 } 759 } 760 } 761 } 762 763 pci_iterator_destroy(iter); 764 765 /* In "probe only" or "configure" mode (signaled by instances being NULL), 766 * our work is done. Return the number of detected devices. 767 */ 768 if (instances == NULL) { 769 return numFound; 770 } 771 772 /* 773 * This may be debatable, but if no PCI devices with a matching vendor 774 * type is found, return zero now. It is probably not desirable to 775 * allow the config file to override this. 776 */ 777 if (allocatedInstances <= 0) { 778 free(instances); 779 return 0; 780 } 781 782 DebugF("%s instances found: %d\n", driverName, allocatedInstances); 783 784 /* 785 * Check for devices that need duplicated instances. This is required 786 * when there is more than one screen per entity. 787 * 788 * XXX This currently doesn't work for cases where the BusID isn't 789 * specified explicitly in the config file. 790 */ 791 792 for (j = 0; j < numDevs; j++) { 793 if (devList[j]->screen > 0 && devList[j]->busID && *devList[j]->busID) { 794 for (i = 0; i < allocatedInstances; i++) { 795 pPci = instances[i].pci; 796 if (xf86ComparePciBusString(devList[j]->busID, 797 PCI_MAKE_BUS(pPci->domain, 798 pPci->bus), pPci->dev, 799 pPci->func)) { 800 allocatedInstances++; 801 instances[allocatedInstances - 1] = instances[i]; 802 instances[allocatedInstances - 1].screen = 803 devList[j]->screen; 804 numFound++; 805 break; 806 } 807 } 808 } 809 } 810 811 for (i = 0; i < allocatedInstances; i++) { 812 GDevPtr dev = NULL; 813 GDevPtr devBus = NULL; 814 815 pPci = instances[i].pci; 816 for (j = 0; j < numDevs; j++) { 817 if (devList[j]->busID && *devList[j]->busID) { 818 if (xf86ComparePciBusString(devList[j]->busID, 819 PCI_MAKE_BUS(pPci->domain, 820 pPci->bus), pPci->dev, 821 pPci->func) && 822 devList[j]->screen == instances[i].screen) { 823 824 if (devBus) 825 xf86MsgVerb(X_WARNING, 0, 826 "%s: More than one matching Device section for " 827 "instances\n\t(BusID: %s) found: %s\n", 828 driverName, devList[j]->busID, 829 devList[j]->identifier); 830 else 831 devBus = devList[j]; 832 } 833 } 834 else { 835 /* 836 * if device section without BusID is found 837 * only assign to it to the primary device. 838 */ 839 if (xf86IsPrimaryPci(pPci)) { 840 xf86Msg(X_PROBED, "Assigning device section with no busID" 841 " to primary device\n"); 842 if (dev || devBus) 843 xf86MsgVerb(X_WARNING, 0, 844 "%s: More than one matching Device section " 845 "found: %s\n", driverName, 846 devList[j]->identifier); 847 else 848 dev = devList[j]; 849 } 850 } 851 } 852 if (devBus) 853 dev = devBus; /* busID preferred */ 854 if (!dev) { 855 if (xf86CheckPciSlot(pPci) && pciDeviceHasBars(pPci)) { 856 xf86MsgVerb(X_WARNING, 0, "%s: No matching Device section " 857 "for instance (BusID PCI:%u@%u:%u:%u) found\n", 858 driverName, pPci->domain, pPci->bus, pPci->dev, 859 pPci->func); 860 } 861 } 862 else { 863 numClaimedInstances++; 864 instances[i].claimed = TRUE; 865 instances[i].dev = dev; 866 } 867 } 868 DebugF("%s instances found: %d\n", driverName, numClaimedInstances); 869 /* 870 * Now check that a chipset or chipID override in the device section 871 * is valid. Chipset has precedence over chipID. 872 * If chipset is not valid ignore BusSlot completely. 873 */ 874 for (i = 0; i < allocatedInstances && numClaimedInstances > 0; i++) { 875 MessageType from = X_PROBED; 876 877 if (!instances[i].claimed) { 878 continue; 879 } 880 if (instances[i].dev->chipset) { 881 for (c = chipsets; c->token >= 0; c++) { 882 if (xf86NameCmp(c->name, instances[i].dev->chipset) == 0) 883 break; 884 } 885 if (c->token == -1) { 886 instances[i].claimed = FALSE; 887 numClaimedInstances--; 888 xf86MsgVerb(X_WARNING, 0, "%s: Chipset \"%s\" in Device " 889 "section \"%s\" isn't valid for this driver\n", 890 driverName, instances[i].dev->chipset, 891 instances[i].dev->identifier); 892 } 893 else { 894 instances[i].chip = c->token; 895 896 for (id = PCIchipsets; id->numChipset >= 0; id++) { 897 if (id->numChipset == instances[i].chip) 898 break; 899 } 900 if (id->numChipset >= 0) { 901 xf86Msg(X_CONFIG, "Chipset override: %s\n", 902 instances[i].dev->chipset); 903 from = X_CONFIG; 904 } 905 else { 906 instances[i].claimed = FALSE; 907 numClaimedInstances--; 908 xf86MsgVerb(X_WARNING, 0, "%s: Chipset \"%s\" in Device " 909 "section \"%s\" isn't a valid PCI chipset\n", 910 driverName, instances[i].dev->chipset, 911 instances[i].dev->identifier); 912 } 913 } 914 } 915 else if (instances[i].dev->chipID > 0) { 916 for (id = PCIchipsets; id->numChipset >= 0; id++) { 917 if (id->PCIid == instances[i].dev->chipID) 918 break; 919 } 920 if (id->numChipset == -1) { 921 instances[i].claimed = FALSE; 922 numClaimedInstances--; 923 xf86MsgVerb(X_WARNING, 0, "%s: ChipID 0x%04X in Device " 924 "section \"%s\" isn't valid for this driver\n", 925 driverName, instances[i].dev->chipID, 926 instances[i].dev->identifier); 927 } 928 else { 929 instances[i].chip = id->numChipset; 930 931 xf86Msg(X_CONFIG, "ChipID override: 0x%04X\n", 932 instances[i].dev->chipID); 933 from = X_CONFIG; 934 } 935 } 936 else if (!instances[i].foundHW) { 937 /* 938 * This means that there was no override and the PCI chipType 939 * doesn't match one that is supported 940 */ 941 instances[i].claimed = FALSE; 942 numClaimedInstances--; 943 } 944 if (instances[i].claimed == TRUE) { 945 for (c = chipsets; c->token >= 0; c++) { 946 if (c->token == instances[i].chip) 947 break; 948 } 949 xf86Msg(from, "Chipset %s found\n", c->name); 950 } 951 } 952 953 /* 954 * Of the claimed instances, check that another driver hasn't already 955 * claimed its slot. 956 */ 957 numFound = 0; 958 for (i = 0; i < allocatedInstances && numClaimedInstances > 0; i++) { 959 if (!instances[i].claimed) 960 continue; 961 pPci = instances[i].pci; 962 963 /* 964 * Allow the same entity to be used more than once for devices with 965 * multiple screens per entity. This assumes implicitly that there 966 * will be a screen == 0 instance. 967 * 968 * XXX Need to make sure that two different drivers don't claim 969 * the same screen > 0 instance. 970 */ 971 if (instances[i].screen == 0 && !xf86CheckPciSlot(pPci)) 972 continue; 973 974 DebugF("%s: card at %d:%d:%d is claimed by a Device section\n", 975 driverName, pPci->bus, pPci->dev, pPci->func); 976 977 /* Allocate an entry in the lists to be returned */ 978 numFound++; 979 retEntities = xnfreallocarray(retEntities, numFound, sizeof(int)); 980 retEntities[numFound - 1] = xf86ClaimPciSlot(pPci, drvp, 981 instances[i].chip, 982 instances[i].dev, 983 instances[i].dev->active); 984 if (retEntities[numFound - 1] == -1 && instances[i].screen > 0) { 985 for (j = 0; j < xf86NumEntities; j++) { 986 EntityPtr pEnt = xf86Entities[j]; 987 988 if (pEnt->bus.type != BUS_PCI) 989 continue; 990 if (pEnt->bus.id.pci == pPci) { 991 retEntities[numFound - 1] = j; 992 xf86AddDevToEntity(j, instances[i].dev); 993 break; 994 } 995 } 996 } 997 } 998 free(instances); 999 if (numFound > 0) { 1000 *foundEntities = retEntities; 1001 } 1002 1003 return numFound; 1004} 1005 1006/* 1007 * xf86ConfigPciEntityInactive() -- This function can be used 1008 * to configure an inactive entity as well as to reconfigure an 1009 * previously active entity inactive. If the entity has been 1010 * assigned to a screen before it will be removed. If p_chip is 1011 * non-NULL all static resources listed there will be registered. 1012 */ 1013static void 1014xf86ConfigPciEntityInactive(EntityInfoPtr pEnt, PciChipsets * p_chip, 1015 EntityProc init, EntityProc enter, 1016 EntityProc leave, void *private) 1017{ 1018 ScrnInfoPtr pScrn; 1019 1020 if ((pScrn = xf86FindScreenForEntity(pEnt->index))) 1021 xf86RemoveEntityFromScreen(pScrn, pEnt->index); 1022 1023 /* shared resources are only needed when entity is active: remove */ 1024 xf86SetEntityFuncs(pEnt->index, init, enter, leave, private); 1025} 1026 1027ScrnInfoPtr 1028xf86ConfigPciEntity(ScrnInfoPtr pScrn, int scrnFlag, int entityIndex, 1029 PciChipsets * p_chip, void *dummy, EntityProc init, 1030 EntityProc enter, EntityProc leave, void *private) 1031{ 1032 EntityInfoPtr pEnt = xf86GetEntityInfo(entityIndex); 1033 1034 if (!pEnt) 1035 return pScrn; 1036 1037 if (!(pEnt->location.type == BUS_PCI) 1038 || !xf86GetPciInfoForEntity(entityIndex)) { 1039 free(pEnt); 1040 return pScrn; 1041 } 1042 if (!pEnt->active) { 1043 xf86ConfigPciEntityInactive(pEnt, p_chip, init, enter, leave, private); 1044 free(pEnt); 1045 return pScrn; 1046 } 1047 1048 if (!pScrn) 1049 pScrn = xf86AllocateScreen(pEnt->driver, scrnFlag); 1050 if (xf86IsEntitySharable(entityIndex)) { 1051 xf86SetEntityShared(entityIndex); 1052 } 1053 xf86AddEntityToScreen(pScrn, entityIndex); 1054 if (xf86IsEntityShared(entityIndex)) { 1055 return pScrn; 1056 } 1057 free(pEnt); 1058 1059 xf86SetEntityFuncs(entityIndex, init, enter, leave, private); 1060 1061 return pScrn; 1062} 1063 1064int 1065xf86VideoPtrToDriverList(struct pci_device *dev, 1066 char *returnList[], int returnListMax) 1067{ 1068 int i; 1069 1070 /* Add more entries here if we ever return more than 4 drivers for 1071 any device */ 1072 const char *driverList[5] = { NULL, NULL, NULL, NULL, NULL }; 1073 1074 switch (dev->vendor_id) { /* AMD Geode LX */ 1075 case 0x1022: 1076 if (dev->device_id == 0x2081) 1077 driverList[0] = "geode"; 1078 break; 1079 /* older Geode products acquired by AMD still carry an NSC vendor_id */ 1080 case 0x100b: 1081 if (dev->device_id == 0x0030) { 1082 /* NSC Geode GX2 specifically */ 1083 driverList[0] = "geode"; 1084 /* GX2 support started its life in the NSC tree and was later 1085 forked by AMD for GEODE so we keep it as a backup */ 1086 driverList[1] = "nsc"; 1087 } 1088 else 1089 /* other NSC variant e.g. 0x0104 (SC1400), 0x0504 (SCx200) */ 1090 driverList[0] = "nsc"; 1091 break; 1092 /* Cyrix Geode GX1 */ 1093 case 0x1078: 1094 if (dev->device_id == 0x0104) 1095 driverList[0] = "cyrix"; 1096 break; 1097 case 0x1142: 1098 driverList[0] = "apm"; 1099 break; 1100 case 0xedd8: 1101 driverList[0] = "ark"; 1102 break; 1103 case 0x1a03: 1104 driverList[0] = "ast"; 1105 break; 1106 case 0x1002: 1107 driverList[0] = "ati"; 1108 break; 1109 case 0x102c: 1110 driverList[0] = "chips"; 1111 break; 1112 case 0x1013: 1113 driverList[0] = "cirrus"; 1114 break; 1115 case 0x3d3d: 1116 driverList[0] = "glint"; 1117 break; 1118 case 0x105d: 1119 driverList[0] = "i128"; 1120 break; 1121 case 0x8086: 1122 switch (dev->device_id) 1123 { 1124 /* Intel i740 */ 1125 case 0x00d1: 1126 case 0x7800: 1127 driverList[0] = "i740"; 1128 break; 1129 /* GMA500/Poulsbo */ 1130 case 0x8108: 1131 case 0x8109: 1132 /* Try psb driver on Poulsbo - if available */ 1133 driverList[0] = "psb"; 1134 driverList[1] = "psb_drv"; 1135 break; 1136 /* GMA600/Oaktrail */ 1137 case 0x4100: 1138 case 0x4101: 1139 case 0x4102: 1140 case 0x4103: 1141 case 0x4104: 1142 case 0x4105: 1143 case 0x4106: 1144 case 0x4107: 1145 /* Atom E620/Oaktrail */ 1146 case 0x4108: 1147 /* Medfield */ 1148 case 0x0130: 1149 case 0x0131: 1150 case 0x0132: 1151 case 0x0133: 1152 case 0x0134: 1153 case 0x0135: 1154 case 0x0136: 1155 case 0x0137: 1156 /* GMA 3600/CDV */ 1157 case 0x0be0: 1158 case 0x0be1: 1159 case 0x0be2: 1160 case 0x0be3: 1161 case 0x0be4: 1162 case 0x0be5: 1163 case 0x0be6: 1164 case 0x0be7: 1165 case 0x0be8: 1166 case 0x0be9: 1167 case 0x0bea: 1168 case 0x0beb: 1169 case 0x0bec: 1170 case 0x0bed: 1171 case 0x0bee: 1172 case 0x0bef: 1173 /* Use fbdev/vesa driver on Oaktrail, Medfield, CDV */ 1174 break; 1175 default: 1176 driverList[0] = "intel"; 1177 break; 1178 } 1179 break; 1180 case 0x102b: 1181 driverList[0] = "mga"; 1182 break; 1183 case 0x10c8: 1184 driverList[0] = "neomagic"; 1185 break; 1186 case 0x10de: 1187 case 0x12d2: 1188 { 1189 int idx = 0; 1190 1191#if defined(__linux__) || defined(__NetBSD__) 1192 driverList[idx++] = "nouveau"; 1193#endif 1194 driverList[idx++] = "nv"; 1195 break; 1196 } 1197 case 0x1106: 1198 driverList[0] = "openchrome"; 1199 break; 1200 case 0x1b36: 1201 driverList[0] = "qxl"; 1202 break; 1203 case 0x1163: 1204 driverList[0] = "rendition"; 1205 break; 1206 case 0x5333: 1207 switch (dev->device_id) { 1208 case 0x88d0: 1209 case 0x88d1: 1210 case 0x88f0: 1211 case 0x8811: 1212 case 0x8812: 1213 case 0x8814: 1214 case 0x8901: 1215 driverList[0] = "s3"; 1216 break; 1217 case 0x5631: 1218 case 0x883d: 1219 case 0x8a01: 1220 case 0x8a10: 1221 case 0x8c01: 1222 case 0x8c03: 1223 case 0x8904: 1224 case 0x8a13: 1225 driverList[0] = "s3virge"; 1226 break; 1227 default: 1228 driverList[0] = "savage"; 1229 break; 1230 } 1231 break; 1232 case 0x1039: 1233 driverList[0] = "sis"; 1234 break; 1235 case 0x126f: 1236 driverList[0] = "siliconmotion"; 1237 break; 1238 case 0x121a: 1239 if (dev->device_id < 0x0003) 1240 driverList[0] = "voodoo"; 1241 else 1242 driverList[0] = "tdfx"; 1243 break; 1244 case 0x1011: 1245 driverList[0] = "tga"; 1246 break; 1247 case 0x1023: 1248 driverList[0] = "trident"; 1249 break; 1250 case 0x100c: 1251 driverList[0] = "tseng"; 1252 break; 1253 case 0x80ee: 1254 driverList[0] = "vboxvideo"; 1255 break; 1256 case 0x15ad: 1257 driverList[0] = "vmware"; 1258 break; 1259 case 0x18ca: 1260 if (dev->device_id == 0x47) 1261 driverList[0] = "xgixp"; 1262 else 1263 driverList[0] = "xgi"; 1264 break; 1265 default: 1266 break; 1267 } 1268 for (i = 0; (i < returnListMax) && (driverList[i] != NULL); i++) { 1269 returnList[i] = xnfstrdup(driverList[i]); 1270 } 1271 return i; /* Number of entries added */ 1272} 1273 1274#ifdef __linux__ 1275static int 1276xchomp(char *line) 1277{ 1278 size_t len = 0; 1279 1280 if (!line) { 1281 return 1; 1282 } 1283 1284 len = strlen(line); 1285 if (line[len - 1] == '\n' && len > 0) { 1286 line[len - 1] = '\0'; 1287 } 1288 return 0; 1289} 1290 1291/* This function is used to provide a workaround for binary drivers that 1292 * don't export their PCI ID's properly. If distros don't end up using this 1293 * feature it can and should be removed because the symbol-based resolution 1294 * scheme should be the primary one */ 1295int 1296xf86MatchDriverFromFiles(uint16_t match_vendor, uint16_t match_chip, 1297 char *matches[], int nmatches) 1298{ 1299 DIR *idsdir; 1300 FILE *fp; 1301 struct dirent *direntry; 1302 char *line = NULL; 1303 size_t len; 1304 ssize_t read; 1305 char path_name[256], vendor_str[5], chip_str[5]; 1306 uint16_t vendor, chip; 1307 int i = 0, j; 1308 1309 idsdir = opendir(PCI_TXT_IDS_PATH); 1310 if (!idsdir) 1311 return 0; 1312 1313 xf86Msg(X_INFO, 1314 "Scanning %s directory for additional PCI ID's supported by the drivers\n", 1315 PCI_TXT_IDS_PATH); 1316 direntry = readdir(idsdir); 1317 /* Read the directory */ 1318 while (direntry) { 1319 if (direntry->d_name[0] == '.') { 1320 direntry = readdir(idsdir); 1321 continue; 1322 } 1323 len = strlen(direntry->d_name); 1324 /* A tiny bit of sanity checking. We should probably do better */ 1325 if (strncmp(&(direntry->d_name[len - 4]), ".ids", 4) == 0) { 1326 /* We need the full path name to open the file */ 1327 snprintf(path_name, sizeof(path_name), "%s/%s", 1328 PCI_TXT_IDS_PATH, direntry->d_name); 1329 fp = fopen(path_name, "r"); 1330 if (fp == NULL) { 1331 xf86Msg(X_ERROR, "Could not open %s for reading. Exiting.\n", 1332 path_name); 1333 goto end; 1334 } 1335 /* Read the file */ 1336#ifdef __GLIBC__ 1337 while ((read = getline(&line, &len, fp)) != -1) { 1338#else 1339 while ((line = fgetln(fp, &len)) != (char *) NULL) { 1340#endif /* __GLIBC __ */ 1341 xchomp(line); 1342 if (isdigit(line[0])) { 1343 strlcpy(vendor_str, line, sizeof(vendor_str)); 1344 vendor = (int) strtol(vendor_str, NULL, 16); 1345 if ((strlen(&line[4])) == 0) { 1346 chip_str[0] = '\0'; 1347 chip = -1; 1348 } 1349 else { 1350 /* Handle trailing whitespace */ 1351 if (isspace(line[4])) { 1352 chip_str[0] = '\0'; 1353 chip = -1; 1354 } 1355 else { 1356 /* Ok, it's a real ID */ 1357 strlcpy(chip_str, &line[4], sizeof(chip_str)); 1358 chip = (int) strtol(chip_str, NULL, 16); 1359 } 1360 } 1361 if (vendor == match_vendor && chip == match_chip) { 1362 matches[i] = 1363 (char *) malloc(sizeof(char) * 1364 strlen(direntry->d_name) - 3); 1365 if (!matches[i]) { 1366 xf86Msg(X_ERROR, 1367 "Could not allocate space for the module name. Exiting.\n"); 1368 goto end; 1369 } 1370 /* hack off the .ids suffix. This should guard 1371 * against other problems, but it will end up 1372 * taking off anything after the first '.' */ 1373 for (j = 0; j < (strlen(direntry->d_name) - 3); j++) { 1374 if (direntry->d_name[j] == '.') { 1375 matches[i][j] = '\0'; 1376 break; 1377 } 1378 else { 1379 matches[i][j] = direntry->d_name[j]; 1380 } 1381 } 1382 xf86Msg(X_INFO, "Matched %s from file name %s\n", 1383 matches[i], direntry->d_name); 1384 i++; 1385 } 1386 } 1387 else { 1388 /* TODO Handle driver overrides here */ 1389 } 1390 } 1391 fclose(fp); 1392 } 1393 direntry = readdir(idsdir); 1394 } 1395 end: 1396 free(line); 1397 closedir(idsdir); 1398 return i; 1399} 1400#endif /* __linux__ */ 1401 1402/** 1403 * @return The numbers of found devices that match with the current system 1404 * drivers. 1405 */ 1406int 1407xf86PciMatchDriver(char *matches[], int nmatches) 1408{ 1409 int i = 0; 1410 struct pci_device *info = NULL; 1411 struct pci_device_iterator *iter; 1412 1413 /* Find the primary device, and get some information about it. */ 1414 iter = pci_slot_match_iterator_create(NULL); 1415 while ((info = pci_device_next(iter)) != NULL) { 1416 if (xf86IsPrimaryPci(info)) { 1417 break; 1418 } 1419 } 1420 1421 pci_iterator_destroy(iter); 1422#ifdef __linux__ 1423 if (info) 1424 i += xf86MatchDriverFromFiles(info->vendor_id, info->device_id, 1425 matches, nmatches); 1426#endif 1427 1428 if ((info != NULL) && (i < nmatches)) { 1429 i += xf86VideoPtrToDriverList(info, &(matches[i]), nmatches - i); 1430 } 1431 1432 return i; 1433} 1434 1435Bool 1436xf86PciConfigure(void *busData, struct pci_device *pDev) 1437{ 1438 struct pci_device *pVideo = NULL; 1439 1440 pVideo = (struct pci_device *) busData; 1441 if (pDev && 1442 (pDev->domain == pVideo->domain) && 1443 (pDev->bus == pVideo->bus) && 1444 (pDev->dev == pVideo->dev) && (pDev->func == pVideo->func)) 1445 return 0; 1446 1447 return 1; 1448} 1449 1450void 1451xf86PciConfigureNewDev(void *busData, struct pci_device *pVideo, 1452 GDevRec * GDev, int *chipset) 1453{ 1454 char busnum[8]; 1455 char *tmp; 1456 1457 pVideo = (struct pci_device *) busData; 1458 1459 if (pVideo->bus < 256) 1460 snprintf(busnum, sizeof(busnum), "%d", pVideo->bus); 1461 else 1462 snprintf(busnum, sizeof(busnum), "%d@%d", 1463 pVideo->bus & 0x00ff, pVideo->bus >> 8); 1464 1465 XNFasprintf(&tmp, "PCI:%s:%d:%d", 1466 busnum, pVideo->dev, pVideo->func); 1467 GDev->busID = tmp; 1468 1469 GDev->chipID = pVideo->device_id; 1470 GDev->chipRev = pVideo->revision; 1471 1472 if (*chipset < 0) 1473 *chipset = (pVideo->vendor_id << 16) | pVideo->device_id; 1474} 1475