/* * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * CONECTIVA LINUX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Conectiva Linux shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from * Conectiva Linux. * * Author: Paulo César Pereira de Andrade * */ #define CARDS_PRIVATE #include "cards.h" #undef SERVER /* defined in config.h, but of no use here */ /* return values from ReadCardsLine. */ #define ERROR -3 #define UNKNOWN -2 #define END -1 #define NOTUSEFUL 0 #define NAME 1 #define CHIPSET 2 #define SERVER 3 #define DRIVER 4 #define RAMDAC 5 #define CLOCKCHIP 6 #define DACSPEED 7 #define NOCLOCKPROBE 8 #define UNSUPPORTED 9 #define SEE 10 #define LINE 11 /* * Prototypes */ static int ReadCardsLine(FILE*, char*); /* must have 256 bytes */ static int CompareCards(_Xconst void *left, _Xconst void *right); static int BCompareCards(_Xconst void *left, _Xconst void *right); static void DoReadCardsDatabase(void); static char **DoFilterCardNames(char *pattern, int *result); #ifdef USE_MODULES typedef struct { int ivendor; unsigned short vendor; unsigned short valid_vendor; char *chipsets; int num_chipsets; } chipset_check; #endif /* * Initialization */ static int linenum = 0; static char *Cards = "lib/X11/Cards"; CardsEntry **CardsDB; int NumCardsEntry; /* * Implementation */ #ifdef USE_MODULES const pciVendorInfo *xf86PCIVendorInfo; #endif #ifdef USE_MODULES void InitializePciInfo(void) { xf86PCIVendorInfo = pciVendorInfoList; } void CheckChipsets(xf86cfgModuleOptions *opts, int *err) { int i, j, ichk, ivnd = 0, vendor = -1, device; const pciDeviceInfo **pDev; SymTabPtr chips = opts->chipsets; chipset_check *check = NULL; int num_check = 0; if (!chips) { CheckMsg(CHECKER_NO_CHIPSETS, "WARNING No chipsets specified.\n"); ++*err; return; } while (chips->name) { device = chips->token & 0xffff; vendor = (chips->token & 0xffff0000) >> 16; if (vendor == 0) vendor = opts->vendor; for (ichk = 0; ichk < num_check; ichk++) if (check[ichk].vendor == vendor) break; if (ichk >= num_check) { check = (chipset_check*) XtRealloc((XtPointer)check, sizeof(chipset_check) * (num_check + 1)); check[num_check].vendor = vendor; memset(&check[num_check], 0, sizeof(chipset_check)); ++num_check; } /* Search for vendor in xf86PCIVendorInfo */ if (xf86PCIVendorInfo) { for (ivnd = 0; xf86PCIVendorInfo[ivnd].VendorID; ivnd++) if (vendor == xf86PCIVendorInfo[ivnd].VendorID) break; } if (xf86PCIVendorInfo && xf86PCIVendorInfo[ivnd].VendorID) { check[ichk].valid_vendor = 1; check[ichk].ivendor = ivnd; } else { CheckMsg(CHECKER_CANNOT_VERIFY_CHIPSET, "WARNING Cannot verify chipset \"%s\" (0x%x)\n", chips->name, device); ++*err; ++chips; continue; } if (xf86PCIVendorInfo && (pDev = xf86PCIVendorInfo[ivnd].Device) != NULL) { if (check[ichk].chipsets == NULL) { for (j = 0; pDev[j]; j++) ; check[ichk].chipsets = (char*)XtCalloc(1, j); } for (j = 0; pDev[j]; j++) { if (device == pDev[j]->DeviceID) { if (strcmp(chips->name, pDev[j]->DeviceName)) { CheckMsg(CHECKER_NOMATCH_CHIPSET_STRINGS, "WARNING chipset strings don't match: \"%s\" \"%s\" (0x%x)\n", chips->name, xf86PCIVendorInfo[ivnd].Device[j]->DeviceName, device); ++*err; } break; } } if (!pDev[j]) { CheckMsg(CHECKER_CHIPSET_NOT_LISTED, "WARNING chipset \"%s\" (0x%x) not in list.\n", chips->name, device); ++*err; } else check[ichk].chipsets[j] = 1; } ++chips; } for (i = 0; i < num_check; i++) { if (!check[i].valid_vendor) { CheckMsg(CHECKER_CHIPSET_NO_VENDOR, "WARNING No such vendor 0x%x\n", vendor); ++*err; } for (j = 0; j < check[i].num_chipsets; j++) { if (xf86PCIVendorInfo && !check[i].chipsets[j]) { CheckMsg(CHECKER_CHIPSET_NOT_SUPPORTED, "NOTICE chipset \"%s\" (0x%x) not listed as supported.\n", xf86PCIVendorInfo[check[i].ivendor].Device[j]->DeviceName, xf86PCIVendorInfo[check[i].ivendor].Device[j]->DeviceID); } } XtFree(check[i].chipsets); } XtFree((XtPointer)check); } #endif void ReadCardsDatabase(void) { #ifdef USE_MODULES if (!nomodules) { int i, j, ivendor, idevice; char name[256]; _Xconst char *vendor, *device; CardsEntry *entry = NULL, *tmp; xf86cfgModuleOptions *opts = module_options; const pciDeviceInfo **pDev; /* Only list cards that have a driver installed */ while (opts) { if (opts->chipsets) { SymTabPtr chips = opts->chipsets; while (chips->name) { vendor = opts->name; device = chips->name; ivendor = (chips->token & 0xffff0000) >> 16; idevice = chips->token & 0xffff0; if (ivendor == 0) ivendor = opts->vendor; if (xf86PCIVendorInfo) { for (i = 0; xf86PCIVendorInfo[i].VendorName; i++) if (ivendor == xf86PCIVendorInfo[i].VendorID) { vendor = xf86PCIVendorInfo[i].VendorName; break; } if (xf86PCIVendorInfo[i].VendorName) { if ((pDev = xf86PCIVendorInfo[i].Device)) { for (j = 0; pDev[j]; j++) if (idevice == pDev[j]->DeviceID) { device = pDev[j]->DeviceName; break; } } } } /* Since frequently there is more than one driver for a * single vendor, it is required to avoid duplicates. */ XmuSnprintf(name, sizeof(name), "%s %s", vendor, device); tmp = LookupCard(name); if (tmp == NULL || strcmp(tmp->chipset, chips->name) || strcmp(tmp->driver, opts->name)) { entry = (CardsEntry*)XtCalloc(1, sizeof(CardsEntry)); if (NumCardsEntry % 16 == 0) { CardsDB = (CardsEntry**)XtRealloc((XtPointer)CardsDB, sizeof(CardsEntry*) * (NumCardsEntry + 16)); } CardsDB[NumCardsEntry++] = entry; entry->name = XtNewString(name); /* XXX no private copy of strings */ entry->chipset = (char*)chips->name; entry->driver = opts->name; /* better than linear searchs to find duplicates */ qsort(CardsDB, NumCardsEntry, sizeof(CardsEntry*), CompareCards); } ++chips; } } opts = opts->next; } /* fix entries with the same name */ for (i = 0; i < NumCardsEntry - 2;) { for (j = i + 1; j < NumCardsEntry - 1 && strcmp(CardsDB[i]->name, CardsDB[j]->name) == 0; j++) ; if (i + 1 != j) { while (i < j) { char *str; if (strcmp(CardsDB[i]->chipset, CardsDB[j]->chipset)) str = CardsDB[i]->chipset; else str = CardsDB[i]->driver; XmuSnprintf(name, sizeof(name), "%s (%s)", CardsDB[i]->name, str); XtFree(CardsDB[i]->name); CardsDB[i]->name = XtNewString(name); ++i; } } else ++i; } /* make sure data is valid to bsearch in */ qsort(CardsDB, NumCardsEntry, sizeof(CardsEntry*), CompareCards); } else #endif DoReadCardsDatabase(); } static void DoReadCardsDatabase(void) { char buffer[256]; FILE *fp = fopen(Cards, "r"); int i, result; CardsEntry *entry = NULL; static char *CardsError = "Error reading Cards database, at line %d (%s).\n"; if (fp == NULL) { fprintf(stderr, "Cannot open Cards database.\n"); exit(1); } while ((result = ReadCardsLine(fp, buffer)) != END) { switch (result) { case ERROR: fprintf(stderr, CardsError, linenum, buffer); break; case UNKNOWN: fprintf(stderr, "Unknown field type in Cards database, at line %d (%s).\n", linenum, buffer); break; case NAME: entry = calloc(1, sizeof(CardsEntry)); if (NumCardsEntry % 16 == 0) { CardsDB = realloc(CardsDB, sizeof(CardsEntry*) * (NumCardsEntry + 16)); if (CardsDB == NULL) { fprintf(stderr, "Out of memory reading Cards database.\n"); exit(1); } } CardsDB[NumCardsEntry++] = entry; entry->name = strdup(buffer); break; case CHIPSET: if (entry == NULL || entry->chipset != NULL) { fprintf(stderr, CardsError, linenum, buffer); } #if 0 else entry->chipset = strdup(buffer); #endif break; case SERVER: if (entry == NULL || entry->server != NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->server = strdup(buffer); break; case DRIVER: if (entry == NULL || entry->driver != NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->driver = strdup(buffer); break; case RAMDAC: if (entry == NULL || entry->ramdac != NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->ramdac = strdup(buffer); break; case CLOCKCHIP: if (entry == NULL || entry->clockchip != NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->clockchip = strdup(buffer); break; case DACSPEED: if (entry == NULL || entry->dacspeed != NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->dacspeed = strdup(buffer); break; case NOCLOCKPROBE: if (entry == NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->flags |= F_NOCLOCKPROBE; break; case UNSUPPORTED: if (entry == NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->flags |= F_UNSUPPORTED; break; case SEE: if (entry == NULL || entry->see != NULL) { fprintf(stderr, CardsError, linenum, buffer); } else entry->see = strdup(buffer); break; case LINE: if (entry == NULL) { fprintf(stderr, CardsError, linenum, buffer); } else if (entry->lines == NULL) entry->lines = strdup(buffer); else { char *str = malloc(strlen(entry->lines) + strlen(buffer) + 2); sprintf(str, "%s\n%s", entry->lines, buffer); free(entry->lines); entry->lines = str; } break; } } fclose(fp); qsort(CardsDB, NumCardsEntry, sizeof(CardsEntry*), CompareCards); #ifdef DEBUG for (i = 0; i < NumCardsEntry - 1; i++) { if (strcmp(CardsDB[i]->name, CardsDB[i+1]->name) == 0) fprintf(stderr, "Duplicate entry in Cards database: (%s).\n", CardsDB[i]->name); } #endif for (i = 0; i < NumCardsEntry - 1; i++) { if (CardsDB[i]->see != NULL) { if ((entry = LookupCard(CardsDB[i]->see)) == NULL) { fprintf(stderr, "Cannot find card '%s' for filling defaults.\n", CardsDB[i]->see); continue; } if (CardsDB[i]->chipset == NULL && entry->chipset != NULL) CardsDB[i]->chipset = strdup(entry->chipset); if (CardsDB[i]->server == NULL && entry->server != NULL) CardsDB[i]->server = strdup(entry->server); if (CardsDB[i]->driver == NULL && entry->driver != NULL) CardsDB[i]->driver = strdup(entry->driver); if (CardsDB[i]->ramdac == NULL && entry->ramdac != NULL) CardsDB[i]->ramdac = strdup(entry->ramdac); if (CardsDB[i]->clockchip == NULL && entry->clockchip != NULL) CardsDB[i]->clockchip = strdup(entry->clockchip); if (CardsDB[i]->dacspeed == NULL && entry->dacspeed != NULL) CardsDB[i]->dacspeed = strdup(entry->dacspeed); if (CardsDB[i]->flags & F_NOCLOCKPROBE) CardsDB[i]->flags |= F_NOCLOCKPROBE; if (CardsDB[i]->flags & F_UNSUPPORTED) CardsDB[i]->flags |= F_UNSUPPORTED; if (entry->lines != NULL) { if (CardsDB[i]->lines == NULL) CardsDB[i]->lines = strdup(entry->lines); else { char *str = malloc(strlen(entry->lines) + strlen(CardsDB[i]->lines) + 2); sprintf(str, "%s\n%s", CardsDB[i]->lines, entry->lines); free(CardsDB[i]->lines); CardsDB[i]->lines = str; } } if (entry->see != NULL) { #ifdef DEBUG fprintf(stderr, "Nested SEE entry: %s -> %s -> %s\n", CardsDB[i]->name, CardsDB[i]->see, entry->see); #endif CardsDB[i]->see = strdup(entry->see); --i; continue; } free(CardsDB[i]->see); CardsDB[i]->see = NULL; } } } CardsEntry * LookupCard(char *name) { CardsEntry **ptr; if (NumCardsEntry == 0 || CardsDB == 0) return NULL; ptr = (CardsEntry**)bsearch(name, CardsDB, NumCardsEntry, sizeof(CardsEntry*), BCompareCards); return (ptr != NULL ? *ptr : NULL); } char ** GetCardNames(int *result) { char **cards = NULL; int ncards; for (ncards = 0; ncards < NumCardsEntry; ncards++) { if (ncards % 16 == 0) { if ((cards = (char**)realloc(cards, sizeof(char*) * (ncards + 16))) == NULL) { fprintf(stderr, "Out of memory.\n"); exit(1); } } cards[ncards] = strdup(CardsDB[ncards]->name); } *result = ncards; return (cards); } char ** FilterCardNames(char *pattern, int *result) { #ifdef USE_MODULES if (!nomodules) { char **cards = NULL; int i, ncards = 0; for (i = 0; i < NumCardsEntry; i++) { if (strstr(CardsDB[i]->name, pattern) == NULL) continue; if (ncards % 16 == 0) { if ((cards = (char**)realloc(cards, sizeof(char*) * (ncards + 16))) == NULL) { fprintf(stderr, "Out of memory.\n"); exit(1); } } cards[ncards] = strdup(CardsDB[i]->name); ++ncards; } *result = ncards; return (cards); } #endif return (DoFilterCardNames(pattern, result)); } static char ** DoFilterCardNames(char *pattern, int *result) { FILE *fp; char **cards = NULL; int len, ncards = 0; char *cmd, *ptr, buffer[256]; cmd = malloc(32 + (strlen(pattern) * 2) + strlen(Cards)); strcpy(cmd, "egrep -i '^NAME\\ .*"); len = strlen(cmd); ptr = pattern; while (*ptr) { if (!isalnum(*ptr)) { cmd[len++] = '\\'; } cmd[len++] = *ptr++; } cmd[len] = '\0'; strcat(cmd, ".*$' "); strcat(cmd, Cards); strcat(cmd, " | sort"); /*sprintf(cmd, "egrep -i '^NAME\\ .*%s.*$' %s | sort", pattern, Cards);*/ if ((fp = popen(cmd, "r")) == NULL) { fprintf(stderr, "Cannot read Cards database.\n"); exit(1); } while (fgets(buffer, sizeof(buffer), fp) != NULL) { ptr = buffer + strlen(buffer) - 1; while (isspace(*ptr) && ptr > buffer) --ptr; if (!isspace(*ptr) && ptr > buffer) ptr[1] = '\0'; ptr = buffer; while (!isspace(*ptr) && *ptr) /* skip NAME */ ++ptr; while (isspace(*ptr) && *ptr) ++ptr; if (ncards % 16 == 0) { if ((cards = (char**)realloc(cards, sizeof(char*) * (ncards + 16))) == NULL) { fprintf(stderr, "Out of memory.\n"); exit(1); } } cards[ncards++] = strdup(ptr); } free(cmd); *result = ncards; return (cards); } static int ReadCardsLine(FILE *fp, char *value) { char name[32], buffer[256], *ptr, *end; int result = NOTUSEFUL; ++linenum; if (fgets(buffer, sizeof(buffer), fp) == NULL) return (END); ptr = buffer; /* skip initial spaces; should'nt bother about this.. */ while (isspace(*ptr) && *ptr) ++ptr; if (*ptr == '#' || *ptr == '\0') return (NOTUSEFUL); end = ptr; while (!isspace(*end) && *end) ++end; if (end - ptr > sizeof(buffer) - 1) { strncpy(value, buffer, 255); value[255] = '\0'; return (ERROR); } strncpy(name, ptr, end - ptr); name[end - ptr] = '\0'; /* read the optional arguments */ ptr = end; while (isspace(*ptr) && *ptr) ++ptr; end = ptr + strlen(ptr) - 1; while (isspace(*end) && end > ptr) --end; if (!isspace(*end)) ++end; *end = '\0'; if (strcmp(name, "NAME") == 0) result = NAME; else if (strcmp(name, "CHIPSET") == 0) result = CHIPSET; else if (strcmp(name, "SERVER") == 0) result = SERVER; else if (strcmp(name, "DRIVER") == 0) result = DRIVER; else if (strcmp(name, "RAMDAC") == 0) result = RAMDAC; else if (strcmp(name, "CLOCKCHIP") == 0) result = CLOCKCHIP; else if (strcmp(name, "DACSPEED") == 0) result = DACSPEED; else if (strcmp(name, "NOCLOCKPROBE") == 0) result = NOCLOCKPROBE; else if (strcmp(name, "UNSUPPORTED") == 0) result = UNSUPPORTED; else if (strcmp(name, "SEE") == 0) result = SEE; else if (strcmp(name, "LINE") == 0) result = LINE; else if (strcmp(name, "END") == 0) result = END; else { strcpy(value, name); return (UNKNOWN); } /* value *must* have at least 256 bytes */ strcpy(value, ptr); return (result); } static int CompareCards(_Xconst void *left, _Xconst void *right) { return strcasecmp((*(CardsEntry**)left)->name, (*(CardsEntry**)right)->name); } static int BCompareCards(_Xconst void *name, _Xconst void *card) { return (strcasecmp((char*)name, (*(CardsEntry**)card)->name)); }