1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/kobj.h> 30 #include <sys/kobj_lex.h> 31 #include <sys/ddi.h> 32 #include <sys/sunddi.h> 33 #include <sys/sunndi.h> 34 35 #define masterfile "/boot/solaris/devicedb/master" 36 37 /* Maximum size of a master line */ 38 #define MASTER_LINE_MAX (1024*2) 39 static char *one_master_line; 40 static int one_master_line_cur_index = 0, one_master_line_max = 0; 41 struct master_line { 42 char *column[10]; 43 char *text; /* to be kmem alloc'd */ 44 int line_size; 45 struct master_line *next, *prev; 46 }; 47 static struct master_line *incore_master_head = NULL, 48 *incore_master_tail = NULL; 49 /* same order as columns in /boot/solaris/devicedb/master */ 50 static int mf_column = 0; 51 static int incore_master_table_line_count = 0; 52 53 #define MASTER_OPS_DEBUG_PRINT_INCORE 0x0001 54 #define MASTER_OPS_DEBUG_PROCESS 0x0002 55 #define MASTER_OPS_DEBUG_LOOKUP 0x0004 56 #define MASTER_OPS_DEBUG_CID_FOUND 0x2000 57 static long master_ops_debug = 0x0; 58 #define EOL 0xA 59 #define FILE_T struct _buf 60 61 static void 62 print_incore_master_table() { 63 struct master_line *ptr = incore_master_head; 64 int i; 65 66 if (master_ops_debug & MASTER_OPS_DEBUG_PRINT_INCORE) { 67 for (i = 0; i < incore_master_table_line_count; i++) { 68 printf("1)%s, 2)%s, 3)%s, 4)%s, 5)%s, 6)%s, ", 69 ptr->column[0], 70 ptr->column[1], 71 ptr->column[2], 72 ptr->column[3], 73 ptr->column[4], 74 ptr->column[5]); 75 if (ptr->column[6] != NULL) { 76 printf("7)%s, ", ptr->column[6]); 77 } 78 if (ptr->column[7] != NULL) { 79 printf("8)%s", ptr->column[7]); 80 } 81 printf("\n"); 82 ptr = ptr->next; 83 if (ptr == NULL) { 84 i++; 85 break; 86 } 87 } 88 printf("There are %d lines\n", i); 89 } 90 } 91 92 /* 93 * parses one_master_line[] from index pointed by one_master_line_cur_index 94 * returns the following tokens 95 * POUND -- if a comment line 96 * NEWLINE -- if an empty line 97 * NAME -- return a string from one_master_line separated by " or blank 98 * EOL -- End of line 99 */ 100 static int 101 master_lex(char *val) { 102 char *cp; 103 int ch; 104 int token; 105 106 cp = val; 107 /* skip leading blanks */ 108 while (((ch = one_master_line[one_master_line_cur_index++]) == ' ' || 109 (ch == '\t')) && (one_master_line_cur_index < one_master_line_max)) 110 ; 111 if ((ch == 0) || (one_master_line_cur_index >= one_master_line_max)) { 112 val = 0; 113 return (EOL); 114 } 115 *cp++ = (char)ch; 116 switch (ch) { 117 case '#': 118 token = POUND; 119 break; 120 case '\n': 121 case '\r': 122 token = NEWLINE; 123 break; 124 case '"': 125 cp--; 126 while (((ch = one_master_line[one_master_line_cur_index++]) 127 != '"') && (one_master_line_cur_index <= 128 one_master_line_max)) { 129 *cp++ = (char)ch; 130 } 131 token = NAME; 132 break; 133 default: 134 ch = one_master_line[one_master_line_cur_index++]; 135 while ((ch != ' ') && (ch != '\t') && (ch != '\n') && 136 (ch != '\r') && (one_master_line_cur_index <= 137 one_master_line_max)) { 138 *cp++ = (char)ch; 139 ch = one_master_line[one_master_line_cur_index++]; 140 } 141 token = NAME; 142 break; 143 } 144 *cp = '\0'; 145 return (token); 146 } 147 148 /* 149 * read a line from devicedb/master file and put it to one_master_line[] buffer 150 * one_master_line_max -- size of data in one_master_line[] 151 * one_master_line_cur_index -- reset to zero 152 */ 153 static int 154 master_get_a_line(FILE_T *file) { 155 int ch; 156 one_master_line_max = 0; 157 one_master_line_cur_index = 0; /* used by master_lex() */ 158 while (((ch = kobj_getc(file)) != '\n') && (ch != '\r')) { 159 if (ch == -1) { 160 if (one_master_line_max == 0) { 161 one_master_line[0] = 0; 162 return (EOF); 163 } else { 164 return (one_master_line_max); 165 } 166 } 167 one_master_line[one_master_line_max++] = ch; 168 if (one_master_line_max >= MASTER_LINE_MAX) { 169 cmn_err(CE_WARN, "!master file line too long:"); 170 cmn_err(CE_CONT, "%s", one_master_line); 171 } 172 } 173 one_master_line[one_master_line_max] = 0; 174 return (one_master_line_max); 175 } 176 177 /* 178 * skip a line 179 */ 180 static void 181 master_skip(FILE_T *file) { 182 char ch; 183 while (((ch = kobj_getc(file)) != '\n') && (ch != '\r')) 184 ; 185 } 186 187 /* 188 * return NULL if no bar found 189 * if bar found, return pointer after the bar 190 * plus, change character '|' (bar) to null as a delimiter 191 */ 192 static char * 193 find_bar(char *target) { 194 if (target == NULL) { 195 return (NULL); 196 } 197 while ((*target != '|') && (*target != ' ') && (*target != 0)) { 198 target++; 199 } 200 if (*target == '|') { 201 *target = 0; 202 return (++target); 203 } 204 return (NULL); 205 } 206 207 /* 208 * If column 0 has | (bars) as device separators, we need make (dup) 209 * more lines for each device. 210 */ 211 static void 212 dup_devices() { 213 struct master_line *last, *ptr = incore_master_tail; 214 char *token; 215 int i; 216 217 if (ptr == NULL || ptr->column == NULL || ptr->column[0] == NULL) { 218 return; 219 } 220 token = incore_master_tail->column[0]; 221 while ((token = find_bar(token)) != NULL) { 222 last = (struct master_line *)kmem_zalloc( 223 sizeof (struct master_line), KM_SLEEP); 224 for (i = 0; i < 10; i++) { 225 last->column[i] = ptr->column[i]; 226 } 227 last->text = ptr->text; 228 last->line_size = 0; /* 'cause we re-use the same line */ 229 last->column[0] = token; 230 ptr->next = last; 231 last->prev = ptr; 232 last->next = NULL; 233 ptr = incore_master_tail = last; 234 incore_master_table_line_count++; 235 } 236 } 237 238 /* 239 * sets master_ops_debug flag from propertyu passed by the boot 240 */ 241 static void 242 set_master_ops_debug_flags() 243 { 244 char *prop; 245 long flags; 246 247 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 248 DDI_PROP_DONTPASS, "master_ops_debug", &prop) == DDI_PROP_SUCCESS) { 249 long data; 250 if (ddi_strtol(prop, NULL, 0, &data) == 0) { 251 master_ops_debug = (unsigned long)data; 252 e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(), 253 "master_ops_debug"); 254 e_ddi_prop_update_int(DDI_DEV_T_NONE, ddi_root_node(), 255 "master_ops_debug", data); 256 } 257 ddi_prop_free(prop); 258 } 259 } 260 261 /* 262 * open / read / parse / close devicedb/master file 263 */ 264 void 265 process_master_file() { 266 FILE_T *file; 267 char tokbuf[MASTER_LINE_MAX]; 268 int token; 269 int done = 0; 270 int line_begin; 271 int x = 0; 272 int total_line_processed = 0; 273 274 set_master_ops_debug_flags(); 275 276 incore_master_head = incore_master_tail = NULL; 277 if ((file = kobj_open_file(masterfile)) == (struct _buf *)-1) { 278 cmn_err(CE_WARN, "!cannot open master file: %s", masterfile); 279 return; 280 } 281 one_master_line = (char *)kmem_zalloc(MASTER_LINE_MAX, KM_SLEEP); 282 master_skip(file); /* skip the first line which is "version" */ 283 mf_column = 0; 284 while (!done) { 285 if (mf_column == 0) { 286 x = master_get_a_line(file); 287 total_line_processed++; 288 if (x == EOF) { /* end of file */ 289 done = 1; 290 continue; 291 } 292 if (x == 0) { /* blank line */ 293 continue; 294 } 295 } 296 token = master_lex(tokbuf); 297 switch (token) { 298 case POUND: /* ignore comment line */ 299 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 300 printf("master_ops: # found skip this line\n"); 301 } 302 mf_column = 0; 303 break; 304 case NAME: /* found actual string, parse and keep it */ 305 if (mf_column == 0) { 306 if (incore_master_tail == NULL) { 307 /* very 1st line */ 308 incore_master_head = 309 incore_master_tail = (struct 310 master_line *) kmem_zalloc( 311 sizeof (struct master_line), 312 KM_SLEEP); 313 incore_master_head->text = (char *) 314 kmem_zalloc(one_master_line_max, 315 KM_SLEEP); 316 incore_master_head->line_size = 317 one_master_line_max; 318 } else { 319 incore_master_tail->next = (struct 320 master_line *)kmem_zalloc( 321 sizeof (struct master_line), 322 KM_SLEEP); 323 incore_master_tail = 324 incore_master_tail->next; 325 incore_master_tail->text = (char *) 326 kmem_zalloc(one_master_line_max, 327 KM_SLEEP); 328 incore_master_tail->line_size = 329 one_master_line_max; 330 } 331 line_begin = 0; 332 incore_master_table_line_count++; 333 } 334 if ((line_begin + strlen(tokbuf) + 1) > 335 MASTER_LINE_MAX) { 336 mf_column = 0; 337 cmn_err(CE_WARN, "!master file line too long"); 338 cmn_err(CE_CONT, "line data: \"%s\"", 339 one_master_line); 340 master_skip(file); /* skip this line */ 341 break; 342 } 343 (void) strcpy(incore_master_tail->text + line_begin, 344 tokbuf); 345 incore_master_tail->column[mf_column] = line_begin + 346 incore_master_tail->text; 347 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 348 printf("master_ops: line=%d column[%x] found:"\ 349 " \"%s\"\n", 350 incore_master_table_line_count, mf_column, 351 incore_master_tail->column[mf_column]); 352 } 353 line_begin += strlen(tokbuf) + 1; 354 mf_column++; 355 break; 356 case EOF: /* end of file */ 357 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 358 printf("master_ops: EOF found. We're done.\n"); 359 } 360 done = 1; 361 break; 362 case EOL: /* end of line */ 363 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 364 printf("master_ops: EOL found.\n"); 365 } 366 mf_column = 0; 367 one_master_line_max = 0; 368 dup_devices(); 369 break; 370 default: /* something went wrong */ 371 cmn_err(CE_WARN, "!master_ops: something went wrong "\ 372 "parsing master file: %s", tokbuf); 373 } 374 } 375 kobj_close_file(file); 376 377 if (master_ops_debug & MASTER_OPS_DEBUG_PROCESS) { 378 printf("master_ops: incore line count: %d\n", 379 incore_master_table_line_count); 380 printf("master_ops: total line processed: %d\n", 381 total_line_processed); 382 } 383 print_incore_master_table(); 384 } 385 386 /* 387 * Loop and free all per line master data, including line text 388 */ 389 void 390 free_master_data() { 391 int i; 392 struct master_line *next, *cur = incore_master_head; 393 for (i = 0; i < incore_master_table_line_count; i++) { 394 next = cur->next; 395 if ((cur->text != NULL) && (cur->line_size != 0)) { 396 kmem_free(cur->text, cur->line_size); 397 } 398 kmem_free(cur, sizeof (struct master_line)); 399 if (next == NULL) { 400 break; /* we're done */ 401 } 402 cur = next; 403 } 404 incore_master_head = incore_master_tail = NULL; 405 if (one_master_line) { 406 kmem_free(one_master_line, MASTER_LINE_MAX); 407 } 408 } 409 410 /* 411 * To match pnpid with master table entries 412 * returns 0 if no matching device found in master file 413 * 1 if a matching device is in master file 414 * devname -- device node name 415 * description -- device description string 416 * properties -- device attributes (e.g. compatibility="kb8042") 417 * (could be NULL) 418 */ 419 int 420 master_file_lookup(char *pnpid, char **devname, char **description, 421 char **properties) 422 { 423 struct master_line *cur = incore_master_head; 424 425 /* 426 * Caller will pass us a null pointer if _CID not present 427 */ 428 if (pnpid == NULL) 429 return (0); 430 431 if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) { 432 printf("master_ops: Looking for %s: ", pnpid); 433 } 434 while (cur != NULL) { 435 if (strcmp(pnpid, cur->column[0]) == 0) { 436 437 *devname = kmem_zalloc(strlen(cur->column[1]) + 1, 438 KM_SLEEP); 439 (void) strcpy(*devname, cur->column[1]); 440 *description = kmem_zalloc(strlen(cur->column[5]) + 1, 441 KM_SLEEP); 442 (void) strcpy(*description, cur->column[5]); 443 if (cur->column[6] != NULL) { 444 *properties = kmem_zalloc( 445 strlen(cur->column[6]) + 1, KM_SLEEP); 446 (void) strcpy(*properties, cur->column[6]); 447 } else 448 *properties = NULL; 449 450 if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) { 451 printf("FOUND. dev node name: \"%s\"\n", 452 *devname); 453 printf("description: \"%s\"", *description); 454 if (*properties != NULL) { 455 printf(" properties: \"%s\"\n", 456 *properties); 457 } else { 458 printf("\n"); 459 } 460 } 461 return (1); 462 } 463 cur = cur->next; 464 } 465 /* XXX: for the devices not found, they should go to used resources?? */ 466 if (master_ops_debug & MASTER_OPS_DEBUG_LOOKUP) { 467 printf("NOT FOUND!!!\n"); 468 } 469 return (0); 470 } 471 472 /* 473 * master_file_lookups() -- processes multiple pnp IDs (CIDs) 474 * return 1 if a PNP id is matched 475 * else return 0 476 */ 477 int 478 master_file_lookups(char *pnpid, char **devname, char **description, 479 char **properties, int pnpid_size) 480 { 481 char *tmp = pnpid; 482 483 if (tmp == NULL) { 484 return (0); 485 } 486 while ((*tmp != NULL) && (tmp <= pnpid + pnpid_size)) { 487 int ret = master_file_lookup(tmp, devname, description, 488 properties); 489 if (ret == 1) { 490 if (master_ops_debug & MASTER_OPS_DEBUG_CID_FOUND) { 491 cmn_err(CE_NOTE, "CID found: %s", tmp); 492 } 493 return (ret); /* a CID is found */ 494 } 495 tmp += strlen(tmp) + 1; /* move on to the next one */ 496 } 497 return (0); 498 } 499