1 /* 2 * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 /*! \brief Implements the driver settings API 7 This file is used by three different components with different needs: 8 1) the boot loader 9 Buffers a list of settings files to move over to the kernel - the 10 actual buffering is located in the boot loader directly, though. 11 Creates driver_settings structures out of those on demand only. 12 2) the kernel 13 Maintains a list of settings so that no disk access is required 14 for known settings (such as those passed over from the boot 15 loader). 16 3) libroot.so 17 Exports the parser to userland applications, so that they can 18 easily make use of driver_settings styled files. 19 20 The file has to be recompiled for every component separately, so that 21 it properly exports the required functionality (which is specified by 22 _BOOT_MODE for the boot loader, and _KERNEL_MODE for the kernel). 23 */ 24 25 #include "fssh_driver_settings.h" 26 27 #include <ctype.h> 28 #include <stdlib.h> 29 30 #include "fssh_fcntl.h" 31 #include "fssh_lock.h" 32 #include "fssh_os.h" 33 #include "fssh_stat.h" 34 #include "fssh_string.h" 35 #include "fssh_unistd.h" 36 37 #include "list.h" 38 39 40 using namespace FSShell; 41 42 #define SETTINGS_DIRECTORY "/kernel/drivers/" 43 #define SETTINGS_MAGIC 'DrvS' 44 45 // Those maximum values are independent from the implementation - they 46 // have been chosen to make the code more robust against bad files 47 #define MAX_SETTINGS_SIZE 32768 48 #define MAX_SETTINGS_LEVEL 8 49 50 #define CONTINUE_PARAMETER 1 51 #define NO_PARAMETER 2 52 53 54 typedef struct settings_handle { 55 list_link link; 56 char name[FSSH_B_OS_NAME_LENGTH]; 57 int32_t ref_count; 58 int32_t magic; 59 struct fssh_driver_settings settings; 60 char *text; 61 } settings_handle; 62 63 64 enum assignment_mode { 65 NO_ASSIGNMENT, 66 ALLOW_ASSIGNMENT, 67 IGNORE_ASSIGNMENT 68 }; 69 70 71 static struct list sHandles; 72 static fssh_mutex sLock; 73 74 75 // #pragma mark - private functions 76 77 78 /*! 79 Returns true for any characters that separate parameters - 80 those are ignored in the input stream and won't be added 81 to any words. 82 */ 83 static inline bool 84 is_parameter_separator(char c) 85 { 86 return c == '\n' || c == ';'; 87 } 88 89 90 /** Indicates if "c" begins a new word or not. 91 */ 92 93 static inline bool 94 is_word_break(char c) 95 { 96 return isspace(c) || is_parameter_separator(c); 97 } 98 99 100 static inline bool 101 check_handle(void *_handle) 102 { 103 settings_handle *handle = (settings_handle *)_handle; 104 if (handle == NULL 105 || handle->magic != SETTINGS_MAGIC) 106 return false; 107 108 return true; 109 } 110 111 112 static fssh_driver_parameter * 113 get_parameter(settings_handle *handle, const char *name) 114 { 115 int32_t i; 116 for (i = handle->settings.parameter_count; i-- > 0;) { 117 if (!fssh_strcmp(handle->settings.parameters[i].name, name)) 118 return &handle->settings.parameters[i]; 119 } 120 return NULL; 121 } 122 123 124 /*! 125 Returns the next word in the input buffer passed in via "_pos" - if 126 this function returns, it will bump the input position after the word. 127 It automatically cares about quoted strings and escaped characters. 128 If "allowNewLine" is true, it reads over comments to get to the next 129 word. 130 Depending on the "assignmentMode" parameter, the '=' sign is either 131 used as a work break, or not. 132 The input buffer will be changed to contain the word without quotes 133 or escaped characters and adds a terminating NULL byte. The "_word" 134 parameter will be set to the beginning of the word. 135 If the word is followed by a newline it will return FSSH_B_OK, if white 136 spaces follows, it will return CONTINUE_PARAMETER. 137 */ 138 static fssh_status_t 139 get_word(char **_pos, char **_word, int32_t assignmentMode, bool allowNewLine) 140 { 141 char *pos = *_pos; 142 char quoted = 0; 143 bool newLine = false, end = false; 144 int escaped = 0; 145 bool charEscaped = false; 146 147 // Skip any white space and comments 148 while (pos[0] 149 && ((allowNewLine && (isspace(pos[0]) || is_parameter_separator(pos[0]) 150 || pos[0] == '#')) 151 || (!allowNewLine && (pos[0] == '\t' || pos[0] == ' ')) 152 || (assignmentMode == ALLOW_ASSIGNMENT && pos[0] == '='))) { 153 // skip any comment lines 154 if (pos[0] == '#') { 155 while (pos[0] && pos[0] != '\n') 156 pos++; 157 } 158 pos++; 159 } 160 161 if (pos[0] == '}' || pos[0] == '\0') { 162 // if we just read some white space before an end of a 163 // parameter, this is just no parameter at all 164 *_pos = pos; 165 return NO_PARAMETER; 166 } 167 168 // Read in a word - might contain escaped (\) spaces, or it 169 // might also be quoted (" or '). 170 171 if (pos[0] == '"' || pos[0] == '\'') { 172 quoted = pos[0]; 173 pos++; 174 } 175 *_word = pos; 176 177 while (pos[0]) { 178 if (charEscaped) 179 charEscaped = false; 180 else if (pos[0] == '\\') { 181 charEscaped = true; 182 escaped++; 183 } else if ((!quoted && (is_word_break(pos[0]) 184 || (assignmentMode != IGNORE_ASSIGNMENT && pos[0] == '='))) 185 || (quoted && pos[0] == quoted)) 186 break; 187 188 pos++; 189 } 190 191 // "String exceeds line" - missing end quote 192 if (quoted && pos[0] != quoted) 193 return FSSH_B_BAD_DATA; 194 195 // last character is a backslash 196 if (charEscaped) 197 return FSSH_B_BAD_DATA; 198 199 end = pos[0] == '\0'; 200 newLine = is_parameter_separator(pos[0]) || end; 201 pos[0] = '\0'; 202 203 // Correct name if there were any escaped characters 204 if (escaped) { 205 char *word = *_word; 206 int offset = 0; 207 while (word <= pos) { 208 if (word[0] == '\\') { 209 offset--; 210 word++; 211 } 212 word[offset] = word[0]; 213 word++; 214 } 215 } 216 217 if (end) { 218 *_pos = pos; 219 return FSSH_B_OK; 220 } 221 222 // Scan for next beginning word, open brackets, or comment start 223 224 pos++; 225 while (true) { 226 *_pos = pos; 227 if (!pos[0]) 228 return FSSH_B_NO_ERROR; 229 230 if (is_parameter_separator(pos[0])) { 231 // an open bracket '{' could follow after the first 232 // newline, but not later 233 if (newLine) 234 return FSSH_B_NO_ERROR; 235 236 newLine = true; 237 } else if (pos[0] == '{' || pos[0] == '}' || pos[0] == '#') 238 return FSSH_B_NO_ERROR; 239 else if (!isspace(pos[0])) 240 return newLine ? FSSH_B_NO_ERROR : CONTINUE_PARAMETER; 241 242 pos++; 243 } 244 } 245 246 247 static fssh_status_t 248 parse_parameter(struct fssh_driver_parameter *parameter, char **_pos, int32_t level) 249 { 250 char *pos = *_pos; 251 fssh_status_t status; 252 253 // initialize parameter first 254 fssh_memset(parameter, 0, sizeof(struct fssh_driver_parameter)); 255 256 status = get_word(&pos, ¶meter->name, NO_ASSIGNMENT, true); 257 if (status == CONTINUE_PARAMETER) { 258 while (status == CONTINUE_PARAMETER) { 259 char **newArray, *value; 260 status = get_word(&pos, &value, parameter->value_count == 0 261 ? ALLOW_ASSIGNMENT : IGNORE_ASSIGNMENT, false); 262 if (status < FSSH_B_OK) 263 break; 264 265 // enlarge value array and save the value 266 267 newArray = (char**)realloc(parameter->values, 268 (parameter->value_count + 1) * sizeof(char *)); 269 if (newArray == NULL) 270 return FSSH_B_NO_MEMORY; 271 272 parameter->values = newArray; 273 parameter->values[parameter->value_count++] = value; 274 } 275 } 276 277 *_pos = pos; 278 return status; 279 } 280 281 282 static fssh_status_t 283 parse_parameters(struct fssh_driver_parameter **_parameters, int *_count, 284 char **_pos, int32_t level) 285 { 286 if (level > MAX_SETTINGS_LEVEL) 287 return FSSH_B_LINK_LIMIT; 288 289 while (true) { 290 struct fssh_driver_parameter parameter; 291 struct fssh_driver_parameter *newArray; 292 fssh_status_t status; 293 294 status = parse_parameter(¶meter, _pos, level); 295 if (status < FSSH_B_OK) 296 return status; 297 298 if (status != NO_PARAMETER) { 299 fssh_driver_parameter *newParameter; 300 301 newArray = (fssh_driver_parameter*)realloc(*_parameters, (*_count + 1) 302 * sizeof(struct fssh_driver_parameter)); 303 if (newArray == NULL) 304 return FSSH_B_NO_MEMORY; 305 306 fssh_memcpy(&newArray[*_count], ¶meter, sizeof(struct fssh_driver_parameter)); 307 newParameter = &newArray[*_count]; 308 309 *_parameters = newArray; 310 (*_count)++; 311 312 // check for level beginning and end 313 if (**_pos == '{') { 314 // if we go a level deeper, just start all over again... 315 (*_pos)++; 316 status = parse_parameters(&newParameter->parameters, 317 &newParameter->parameter_count, _pos, level + 1); 318 if (status < FSSH_B_OK) 319 return status; 320 } 321 } 322 323 if ((**_pos == '}' && level > 0) 324 || (**_pos == '\0' && level == 0)) { 325 // take the closing bracket from the stack 326 (*_pos)++; 327 return FSSH_B_OK; 328 } 329 330 // obviously, something has gone wrong 331 if (**_pos == '}' || **_pos == '\0') 332 return FSSH_B_ERROR; 333 } 334 } 335 336 337 static fssh_status_t 338 parse_settings(settings_handle *handle) 339 { 340 char *text = handle->text; 341 342 fssh_memset(&handle->settings, 0, sizeof(struct fssh_driver_settings)); 343 344 // empty settings are allowed 345 if (text == NULL) 346 return FSSH_B_OK; 347 348 return parse_parameters(&handle->settings.parameters, 349 &handle->settings.parameter_count, &text, 0); 350 } 351 352 353 static void 354 free_parameter(struct fssh_driver_parameter *parameter) 355 { 356 int32_t i; 357 for (i = parameter->parameter_count; i-- > 0;) 358 free_parameter(¶meter->parameters[i]); 359 360 free(parameter->parameters); 361 free(parameter->values); 362 } 363 364 365 static void 366 free_settings(settings_handle *handle) 367 { 368 int32_t i; 369 for (i = handle->settings.parameter_count; i-- > 0;) 370 free_parameter(&handle->settings.parameters[i]); 371 372 free(handle->settings.parameters); 373 free(handle->text); 374 free(handle); 375 } 376 377 378 static settings_handle * 379 new_settings(char *buffer, const char *driverName) 380 { 381 settings_handle *handle = (settings_handle*)malloc(sizeof(settings_handle)); 382 if (handle == NULL) 383 return NULL; 384 385 handle->magic = SETTINGS_MAGIC; 386 handle->text = buffer; 387 388 fssh_strlcpy(handle->name, driverName, sizeof(handle->name)); 389 390 if (parse_settings(handle) == FSSH_B_OK) 391 return handle; 392 393 free(handle); 394 return NULL; 395 } 396 397 398 static settings_handle * 399 load_driver_settings_from_file(int file, const char *driverName) 400 { 401 struct fssh_stat stat; 402 403 // Allocate a buffer and read the whole file into it. 404 // We will keep this buffer in memory, until the settings 405 // are unloaded. 406 // The fssh_driver_parameter::name field will point directly 407 // to this buffer. 408 409 if (fssh_fstat(file, &stat) < FSSH_B_OK) 410 return NULL; 411 412 if (stat.fssh_st_size > FSSH_B_OK && stat.fssh_st_size < MAX_SETTINGS_SIZE) { 413 char *text = (char *)malloc(stat.fssh_st_size + 1); 414 if (text != NULL && fssh_read(file, text, stat.fssh_st_size) == stat.fssh_st_size) { 415 settings_handle *handle; 416 417 text[stat.fssh_st_size] = '\0'; 418 // make sure the string is null terminated 419 // to avoid misbehaviour 420 421 handle = new_settings(text, driverName); 422 if (handle != NULL) { 423 // everything went fine! 424 return handle; 425 } 426 427 free(handle); 428 } 429 // "text" might be NULL here, but that's allowed 430 free(text); 431 } 432 433 return NULL; 434 } 435 436 437 static bool 438 put_string(char **_buffer, fssh_size_t *_bufferSize, char *string) 439 { 440 fssh_size_t length, reserved, quotes; 441 char *buffer = *_buffer, c; 442 bool quoted; 443 444 if (string == NULL) 445 return true; 446 447 for (length = reserved = quotes = 0; (c = string[length]) != '\0'; length++) { 448 if (c == '"') 449 quotes++; 450 else if (is_word_break(c)) 451 reserved++; 452 } 453 quoted = reserved || quotes; 454 455 // update _bufferSize in any way, so that we can chain several 456 // of these calls without having to check the return value 457 // everytime 458 *_bufferSize -= length + (quoted ? 2 + quotes : 0); 459 460 if (*_bufferSize <= 0) 461 return false; 462 463 if (quoted) 464 *(buffer++) = '"'; 465 466 for (;(c = string[0]) != '\0'; string++) { 467 if (c == '"') 468 *(buffer++) = '\\'; 469 470 *(buffer++) = c; 471 } 472 473 if (quoted) 474 *(buffer++) = '"'; 475 476 buffer[0] = '\0'; 477 478 // update the buffer position 479 *_buffer = buffer; 480 481 return true; 482 } 483 484 485 static bool 486 put_chars(char **_buffer, fssh_size_t *_bufferSize, const char *chars) 487 { 488 char *buffer = *_buffer; 489 fssh_size_t length; 490 491 if (chars == NULL) 492 return true; 493 494 length = fssh_strlen(chars); 495 *_bufferSize -= length; 496 497 if (*_bufferSize <= 0) 498 return false; 499 500 fssh_memcpy(buffer, chars, length); 501 buffer += length; 502 buffer[0] = '\0'; 503 504 // update the buffer position 505 *_buffer = buffer; 506 507 return true; 508 } 509 510 511 static bool 512 put_char(char **_buffer, fssh_size_t *_bufferSize, char c) 513 { 514 char *buffer = *_buffer; 515 516 *_bufferSize -= 1; 517 518 if (*_bufferSize <= 0) 519 return false; 520 521 buffer[0] = c; 522 buffer[1] = '\0'; 523 524 // update the buffer position 525 *_buffer = buffer + 1; 526 527 return true; 528 } 529 530 531 static void 532 put_level_space(char **_buffer, fssh_size_t *_bufferSize, int32_t level) 533 { 534 while (level-- > 0) 535 put_char(_buffer, _bufferSize, '\t'); 536 } 537 538 539 static bool 540 put_parameter(char **_buffer, fssh_size_t *_bufferSize, 541 struct fssh_driver_parameter *parameter, int32_t level, bool flat) 542 { 543 int32_t i; 544 545 if (!flat) 546 put_level_space(_buffer, _bufferSize, level); 547 548 put_string(_buffer, _bufferSize, parameter->name); 549 if (flat && parameter->value_count > 0) 550 put_chars(_buffer, _bufferSize, " ="); 551 552 for (i = 0; i < parameter->value_count; i++) { 553 put_char(_buffer, _bufferSize, ' '); 554 put_string(_buffer, _bufferSize, parameter->values[i]); 555 } 556 557 if (parameter->parameter_count > 0) { 558 put_chars(_buffer, _bufferSize, " {"); 559 if (!flat) 560 put_char(_buffer, _bufferSize, '\n'); 561 562 for (i = 0; i < parameter->parameter_count; i++) { 563 put_parameter(_buffer, _bufferSize, ¶meter->parameters[i], 564 level + 1, flat); 565 566 if (parameter->parameters[i].parameter_count == 0) 567 put_chars(_buffer, _bufferSize, flat ? "; " : "\n"); 568 } 569 570 if (!flat) 571 put_level_space(_buffer, _bufferSize, level); 572 put_chars(_buffer, _bufferSize, flat ? "}" : "}\n"); 573 } 574 575 return *_bufferSize >= 0; 576 } 577 578 579 // #pragma mark - Kernel only functions 580 581 582 static settings_handle * 583 find_driver_settings(const char *name) 584 { 585 settings_handle *handle = NULL; 586 587 FSSH_ASSERT_LOCKED_MUTEX(&sLock); 588 589 while ((handle = (settings_handle*)list_get_next_item(&sHandles, handle)) != NULL) { 590 if (!fssh_strcmp(handle->name, name)) 591 return handle; 592 } 593 594 return NULL; 595 } 596 597 598 namespace FSShell { 599 600 fssh_status_t 601 driver_settings_init() 602 { 603 fssh_mutex_init(&sLock, "driver settings"); 604 return FSSH_B_OK; 605 } 606 607 } // namespace FSShell 608 609 610 // #pragma mark - public API 611 612 613 fssh_status_t 614 fssh_unload_driver_settings(void *handle) 615 { 616 if (!check_handle(handle)) 617 return FSSH_B_BAD_VALUE; 618 619 #if 0 620 fssh_mutex_lock(&sLock); 621 // ToDo: as soon as "/boot" is accessible, we should start throwing away settings 622 if (--handle->ref_count == 0) { 623 list_remove_link(&handle->link); 624 } else 625 handle = NULL; 626 fssh_mutex_unlock(&sLock); 627 #endif 628 629 if (handle != NULL) 630 free_settings((settings_handle*)handle); 631 632 return FSSH_B_OK; 633 } 634 635 636 void * 637 fssh_load_driver_settings(const char *driverName) 638 { 639 settings_handle *handle; 640 int file = -1; 641 642 if (driverName == NULL) 643 return NULL; 644 645 // see if we already have these settings loaded 646 fssh_mutex_lock(&sLock); 647 handle = find_driver_settings(driverName); 648 if (handle != NULL) { 649 handle->ref_count++; 650 651 // we got it, now let's see if it already has been parsed 652 if (handle->magic != SETTINGS_MAGIC) { 653 handle->magic = SETTINGS_MAGIC; 654 655 if (parse_settings(handle) != FSSH_B_OK) { 656 // no valid settings, let's cut down its memory requirements 657 free(handle->text); 658 handle->text = NULL; 659 handle = NULL; 660 } 661 } 662 fssh_mutex_unlock(&sLock); 663 return handle; 664 } 665 666 // open the settings from the standardized location 667 if (driverName[0] != '/') { 668 char path[FSSH_B_FILE_NAME_LENGTH + 64]; 669 670 // This location makes at least a bit sense under BeOS compatible 671 // systems. 672 fssh_strcpy(path, "/boot/home/config/settings/fs_shell"); 673 674 { 675 fssh_strlcat(path, SETTINGS_DIRECTORY, sizeof(path)); 676 fssh_strlcat(path, driverName, sizeof(path)); 677 } 678 679 file = fssh_open(path, FSSH_O_RDONLY); 680 } else 681 file = fssh_open(driverName, FSSH_O_RDONLY); 682 683 if (file < FSSH_B_OK) { 684 fssh_mutex_unlock(&sLock); 685 return NULL; 686 } 687 688 handle = load_driver_settings_from_file(file, driverName); 689 690 if (handle != NULL) 691 list_add_item(&sHandles, handle); 692 fssh_mutex_unlock(&sLock); 693 694 fssh_close(file); 695 return (void *)handle; 696 } 697 698 699 /** Loads a driver settings file using the full path, instead of 700 * only defining the leaf name (as load_driver_settings() does). 701 * I am not sure if this function is really necessary - I would 702 * probably prefer something like a search order (if it's not 703 * an absolute path): 704 * ~/config/settings/kernel/driver 705 * current directory 706 * That would render this function useless. 707 */ 708 709 #if 0 710 void * 711 fssh_load_driver_settings_from_path(const char *path) 712 { 713 settings_handle *handle; 714 int file; 715 716 if (path == NULL) 717 return NULL; 718 719 file = fssh_open(path, FSSH_O_RDONLY); 720 if (file < FSSH_B_OK) 721 return NULL; 722 723 handle = load_driver_settings_from_file(file); 724 725 fssh_close(file); 726 return (void *)handle; 727 } 728 #endif 729 730 731 /*! 732 Returns a new driver_settings handle that has the parsed contents 733 of the passed string. 734 You can get an empty driver_settings object when you pass NULL as 735 the "settingsString" parameter. 736 */ 737 void * 738 fssh_parse_driver_settings_string(const char *settingsString) 739 { 740 // we simply copy the whole string to use it as our internal buffer 741 char *text = fssh_strdup(settingsString); 742 if (settingsString == NULL || text != NULL) { 743 settings_handle *handle = (settings_handle*)malloc(sizeof(settings_handle)); 744 if (handle != NULL) { 745 handle->magic = SETTINGS_MAGIC; 746 handle->text = text; 747 748 if (parse_settings(handle) == FSSH_B_OK) 749 return handle; 750 751 free(handle); 752 } 753 free(text); 754 } 755 756 return NULL; 757 } 758 759 760 /*! 761 This function prints out a driver settings structure to a human 762 readable string. 763 It's either in standard style or the single line style speficied 764 by the "flat" parameter. 765 If the buffer is too small to hold the string, FSSH_B_BUFFER_OVERFLOW 766 is returned, and the needed amount of bytes if placed in the 767 "_bufferSize" parameter. 768 If the "handle" parameter is not a valid driver settings handle, or 769 the "buffer" parameter is NULL, FSSH_B_BAD_VALUE is returned. 770 */ 771 fssh_status_t 772 fssh_get_driver_settings_string(void *_handle, char *buffer, 773 fssh_size_t *_bufferSize, bool flat) 774 { 775 settings_handle *handle = (settings_handle *)_handle; 776 fssh_size_t bufferSize = *_bufferSize; 777 int32_t i; 778 779 if (!check_handle(handle) || !buffer || *_bufferSize == 0) 780 return FSSH_B_BAD_VALUE; 781 782 for (i = 0; i < handle->settings.parameter_count; i++) { 783 put_parameter(&buffer, &bufferSize, &handle->settings.parameters[i], 784 0, flat); 785 } 786 787 *_bufferSize -= bufferSize; 788 return bufferSize >= 0 ? FSSH_B_OK : FSSH_B_BUFFER_OVERFLOW; 789 } 790 791 792 /*! 793 Matches the first value of the parameter matching "keyName" with a set 794 of boolean values like 1/true/yes/on/enabled/... 795 Returns "unknownValue" if the parameter could not be found or doesn't 796 have any valid boolean setting, and "noArgValue" if the parameter 797 doesn't have any values. 798 Also returns "unknownValue" if the handle passed in was not valid. 799 */ 800 bool 801 fssh_get_driver_boolean_parameter(void *handle, const char *keyName, 802 bool unknownValue, bool noArgValue) 803 { 804 fssh_driver_parameter *parameter; 805 char *boolean; 806 807 if (!check_handle(handle)) 808 return unknownValue; 809 810 // check for the parameter 811 if ((parameter = get_parameter((settings_handle*)handle, keyName)) == NULL) 812 return unknownValue; 813 814 // check for the argument 815 if (parameter->value_count <= 0) 816 return noArgValue; 817 818 boolean = parameter->values[0]; 819 if (!fssh_strcmp(boolean, "1") 820 || !fssh_strcasecmp(boolean, "true") 821 || !fssh_strcasecmp(boolean, "yes") 822 || !fssh_strcasecmp(boolean, "on") 823 || !fssh_strcasecmp(boolean, "enable") 824 || !fssh_strcasecmp(boolean, "enabled")) 825 return true; 826 827 if (!fssh_strcmp(boolean, "0") 828 || !fssh_strcasecmp(boolean, "false") 829 || !fssh_strcasecmp(boolean, "no") 830 || !fssh_strcasecmp(boolean, "off") 831 || !fssh_strcasecmp(boolean, "disable") 832 || !fssh_strcasecmp(boolean, "disabled")) 833 return false; 834 835 // if no known keyword is found, "unknownValue" is returned 836 return unknownValue; 837 } 838 839 840 const char * 841 fssh_get_driver_parameter(void *handle, const char *keyName, 842 const char *unknownValue, const char *noArgValue) 843 { 844 struct fssh_driver_parameter *parameter; 845 846 if (!check_handle(handle)) 847 return unknownValue; 848 849 // check for the parameter 850 if ((parameter = get_parameter((settings_handle*)handle, keyName)) == NULL) 851 return unknownValue; 852 853 // check for the argument 854 if (parameter->value_count <= 0) 855 return noArgValue; 856 857 return parameter->values[0]; 858 } 859 860 861 const fssh_driver_settings * 862 fssh_get_driver_settings(void *handle) 863 { 864 if (!check_handle(handle)) 865 return NULL; 866 867 return &((settings_handle *)handle)->settings; 868 } 869 870 871 fssh_status_t 872 fssh_delete_driver_settings(void *handle) 873 { 874 return fssh_unload_driver_settings(handle); 875 } 876