1 /* 2 * The PCI Library -- Parsing of the ID list 3 * 4 * Copyright (c) 1997--2008 Martin Mares <[email protected]> 5 * 6 * Can be freely distributed and used under the terms of the GNU GPL v2+. 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <errno.h> 15 16 #include "internal.h" 17 #include "names.h" 18 19 #ifdef PCI_COMPRESSED_IDS 20 #include <zlib.h> 21 typedef gzFile pci_file; 22 #define pci_gets(f, l, s) gzgets(f, l, s) 23 #define pci_eof(f) gzeof(f) 24 25 static pci_file pci_open(struct pci_access *a) 26 { 27 pci_file result; 28 size_t len; 29 char *new_name; 30 31 result = gzopen(a->id_file_name, "rb"); 32 if (result) 33 return result; 34 len = strlen(a->id_file_name); 35 if (len < 3 || memcmp(a->id_file_name + len - 3, ".gz", 3) != 0) 36 return result; 37 new_name = malloc(len - 2); 38 memcpy(new_name, a->id_file_name, len - 3); 39 new_name[len - 3] = 0; 40 pci_set_name_list_path(a, new_name, 1); 41 return gzopen(a->id_file_name, "rb"); 42 } 43 44 #define pci_close(f) gzclose(f) 45 #define PCI_ERROR(f, err) \ 46 if (!err) { \ 47 int errnum = 0; \ 48 gzerror(f, &errnum); \ 49 if (errnum >= 0) err = NULL; \ 50 else if (errnum == Z_ERRNO) err = "I/O error"; \ 51 else err = zError(errnum); \ 52 } 53 #else 54 typedef FILE * pci_file; 55 #define pci_gets(f, l, s) fgets(l, s, f) 56 #define pci_eof(f) feof(f) 57 #define pci_open(a) fopen(a->id_file_name, "r") 58 #define pci_close(f) fclose(f) 59 #define PCI_ERROR(f, err) if (!err && ferror(f)) err = "I/O error"; 60 #endif 61 62 static int id_hex(char *p, int cnt) 63 { 64 int x = 0; 65 while (cnt--) 66 { 67 x <<= 4; 68 if (*p >= '0' && *p <= '9') 69 x += (*p - '0'); 70 else if (*p >= 'a' && *p <= 'f') 71 x += (*p - 'a' + 10); 72 else if (*p >= 'A' && *p <= 'F') 73 x += (*p - 'A' + 10); 74 else 75 return -1; 76 p++; 77 } 78 return x; 79 } 80 81 static inline int id_white_p(int c) 82 { 83 return (c == ' ') || (c == '\t'); 84 } 85 86 87 static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) 88 { 89 char line[MAX_LINE]; 90 char *p; 91 int id1=0, id2=0, id3=0, id4=0; 92 int cat = -1; 93 int nest; 94 static const char parse_error[] = "Parse error"; 95 96 *lino = 0; 97 while (pci_gets(f, line, sizeof(line))) 98 { 99 (*lino)++; 100 p = line; 101 while (*p && *p != '\n' && *p != '\r') 102 p++; 103 if (!*p && !pci_eof(f)) 104 return "Line too long"; 105 *p = 0; 106 if (p > line && (p[-1] == ' ' || p[-1] == '\t')) 107 *--p = 0; 108 109 p = line; 110 while (id_white_p(*p)) 111 p++; 112 if (!*p || *p == '#') 113 continue; 114 115 p = line; 116 while (*p == '\t') 117 p++; 118 nest = p - line; 119 120 if (!nest) /* Top-level entries */ 121 { 122 if (p[0] == 'C' && p[1] == ' ') /* Class block */ 123 { 124 if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4])) 125 return parse_error; 126 cat = ID_CLASS; 127 p += 5; 128 } 129 else if (p[0] == 'S' && p[1] == ' ') 130 { /* Generic subsystem block */ 131 if ((id1 = id_hex(p+2, 4)) < 0 || p[6]) 132 return parse_error; 133 if (!pci_id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0)) 134 return "Vendor does not exist"; 135 cat = ID_GEN_SUBSYSTEM; 136 continue; 137 } 138 else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ') 139 { /* Unrecognized block (RFU) */ 140 cat = ID_UNKNOWN; 141 continue; 142 } 143 else /* Vendor ID */ 144 { 145 if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) 146 return parse_error; 147 cat = ID_VENDOR; 148 p += 5; 149 } 150 id2 = id3 = id4 = 0; 151 } 152 else if (cat == ID_UNKNOWN) /* Nested entries in RFU blocks are skipped */ 153 continue; 154 else if (nest == 1) /* Nesting level 1 */ 155 switch (cat) 156 { 157 case ID_VENDOR: 158 case ID_DEVICE: 159 case ID_SUBSYSTEM: 160 if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) 161 return parse_error; 162 p += 5; 163 cat = ID_DEVICE; 164 id3 = id4 = 0; 165 break; 166 case ID_GEN_SUBSYSTEM: 167 if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) 168 return parse_error; 169 p += 5; 170 id3 = id4 = 0; 171 break; 172 case ID_CLASS: 173 case ID_SUBCLASS: 174 case ID_PROGIF: 175 if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) 176 return parse_error; 177 p += 3; 178 cat = ID_SUBCLASS; 179 id3 = id4 = 0; 180 break; 181 default: 182 return parse_error; 183 } 184 else if (nest == 2) /* Nesting level 2 */ 185 switch (cat) 186 { 187 case ID_DEVICE: 188 case ID_SUBSYSTEM: 189 if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9])) 190 return parse_error; 191 p += 10; 192 cat = ID_SUBSYSTEM; 193 break; 194 case ID_CLASS: 195 case ID_SUBCLASS: 196 case ID_PROGIF: 197 if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) 198 return parse_error; 199 p += 3; 200 cat = ID_PROGIF; 201 id4 = 0; 202 break; 203 default: 204 return parse_error; 205 } 206 else /* Nesting level 3 or more */ 207 return parse_error; 208 while (id_white_p(*p)) 209 p++; 210 if (!*p) 211 return parse_error; 212 if (pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL)) 213 return "Duplicate entry"; 214 } 215 return NULL; 216 } 217 218 int 219 pci_load_name_list(struct pci_access *a) 220 { 221 pci_file f; 222 int lino; 223 const char *err; 224 225 pci_free_name_list(a); 226 a->id_load_attempted = 1; 227 if (!(f = pci_open(a))) 228 return 0; 229 err = id_parse_list(a, f, &lino); 230 PCI_ERROR(f, err); 231 pci_close(f); 232 if (err) 233 a->error("%s at %s, line %d\n", err, a->id_file_name, lino); 234 return 1; 235 } 236 237 void 238 pci_free_name_list(struct pci_access *a) 239 { 240 pci_id_cache_flush(a); 241 pci_id_hash_free(a); 242 pci_id_hwdb_free(a); 243 a->id_load_attempted = 0; 244 } 245 246 void 247 pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) 248 { 249 if (a->free_id_name) 250 free(a->id_file_name); 251 a->id_file_name = name; 252 a->free_id_name = to_be_freed; 253 } 254