vfs: check userland buffers before reading them.
[haiku.git] / src / system / libroot / os / driver_settings.cpp
blob9004095dac6338e2b5f0d243f173fe8f3545c345
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 */
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.
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).
25 // The boot loader is compiled with kernel rules, but we want to explicitely
26 // differentiate between the two here.
27 #ifdef _BOOT_MODE
28 # undef _KERNEL_MODE
29 #endif
31 #include <directories.h>
32 #include <driver_settings.h>
33 #include <FindDirectory.h>
34 #include <OS.h>
36 #ifdef _KERNEL_MODE
37 # include <KernelExport.h>
38 # include <util/list.h>
39 # include <lock.h>
40 # include <kdriver_settings.h>
41 # include <kernel.h>
42 # include <boot/kernel_args.h>
43 # include <boot_device.h>
44 #endif
45 #ifdef _BOOT_MODE
46 # include <boot/kernel_args.h>
47 # include <boot/stage2.h>
48 #else
49 # include <find_directory_private.h>
50 #endif
52 #include <stdlib.h>
53 #include <string.h>
54 #include <strings.h>
55 #include <unistd.h>
56 #include <fcntl.h>
57 #include <ctype.h>
59 #ifndef B_BUFFER_OVERFLOW
60 # define B_BUFFER_OVERFLOW B_ERROR
61 #endif
63 #define SETTINGS_DIRECTORY "/kernel/drivers/"
64 #define SETTINGS_MAGIC 'DrvS'
66 // Those maximum values are independent from the implementation - they
67 // have been chosen to make the code more robust against bad files
68 #define MAX_SETTINGS_SIZE 32768
69 #define MAX_SETTINGS_LEVEL 8
71 #define CONTINUE_PARAMETER 1
72 #define NO_PARAMETER 2
75 typedef struct settings_handle {
76 #ifdef _KERNEL_MODE
77 list_link link;
78 char name[B_OS_NAME_LENGTH];
79 int32 ref_count;
80 // A negative ref_count means the node is not reference counted and not
81 // stored in the list.
82 #endif
83 int32 magic;
84 struct driver_settings settings;
85 char *text;
86 } settings_handle;
89 enum assignment_mode {
90 NO_ASSIGNMENT,
91 ALLOW_ASSIGNMENT,
92 IGNORE_ASSIGNMENT
96 #ifdef _KERNEL_MODE
97 static struct list sHandles;
98 static mutex sLock = MUTEX_INITIALIZER("driver settings");
99 #endif
102 // #pragma mark - private functions
106 \brief Returns true for any characters that separate parameters
108 Those characters are ignored in the input stream and won't be added
109 to any words.
111 static inline bool
112 is_parameter_separator(char c)
114 return c == '\n' || c == ';';
118 /*! Indicates if "c" begins a new word or not.
120 static inline bool
121 is_word_break(char c)
123 return isspace(c) || is_parameter_separator(c);
127 static inline bool
128 check_handle(settings_handle *handle)
130 if (handle == NULL
131 || handle->magic != SETTINGS_MAGIC)
132 return false;
134 return true;
138 static driver_parameter *
139 get_parameter(settings_handle *handle, const char *name)
141 int32 i;
142 for (i = handle->settings.parameter_count; i-- > 0;) {
143 if (!strcmp(handle->settings.parameters[i].name, name))
144 return &handle->settings.parameters[i];
146 return NULL;
151 Returns the next word in the input buffer passed in via "_pos" - if
152 this function returns, it will bump the input position after the word.
153 It automatically cares about quoted strings and escaped characters.
154 If "allowNewLine" is true, it reads over comments to get to the next
155 word.
156 Depending on the "assignmentMode" parameter, the '=' sign is either
157 used as a work break, or not.
158 The input buffer will be changed to contain the word without quotes
159 or escaped characters and adds a terminating NULL byte. The "_word"
160 parameter will be set to the beginning of the word.
161 If the word is followed by a newline it will return B_OK, if white
162 spaces follows, it will return CONTINUE_PARAMETER.
164 static status_t
165 get_word(char **_pos, char **_word, int32 assignmentMode, bool allowNewLine)
167 char *pos = *_pos;
168 char quoted = 0;
169 bool newLine = false, end = false;
170 int escaped = 0;
171 bool charEscaped = false;
173 // Skip any white space and comments
174 while (pos[0]
175 && ((allowNewLine && (isspace(pos[0]) || is_parameter_separator(pos[0])
176 || pos[0] == '#'))
177 || (!allowNewLine && (pos[0] == '\t' || pos[0] == ' '))
178 || (assignmentMode == ALLOW_ASSIGNMENT && pos[0] == '='))) {
179 // skip any comment lines
180 if (pos[0] == '#') {
181 while (pos[0] && pos[0] != '\n')
182 pos++;
184 pos++;
187 if (pos[0] == '}' || pos[0] == '\0') {
188 // if we just read some white space before an end of a
189 // parameter, this is just no parameter at all
190 *_pos = pos;
191 return NO_PARAMETER;
194 // Read in a word - might contain escaped (\) spaces, or it
195 // might also be quoted (" or ').
197 if (pos[0] == '"' || pos[0] == '\'') {
198 quoted = pos[0];
199 pos++;
201 *_word = pos;
203 while (pos[0]) {
204 if (charEscaped)
205 charEscaped = false;
206 else if (pos[0] == '\\') {
207 charEscaped = true;
208 escaped++;
209 } else if ((!quoted && (is_word_break(pos[0])
210 || (assignmentMode != IGNORE_ASSIGNMENT && pos[0] == '=')))
211 || (quoted && pos[0] == quoted))
212 break;
214 pos++;
217 // "String exceeds line" - missing end quote
218 if (quoted && pos[0] != quoted)
219 return B_BAD_DATA;
221 // last character is a backslash
222 if (charEscaped)
223 return B_BAD_DATA;
225 end = pos[0] == '\0';
226 newLine = is_parameter_separator(pos[0]) || end;
227 pos[0] = '\0';
229 // Correct name if there were any escaped characters
230 if (escaped) {
231 char *word = *_word;
232 int offset = 0;
233 while (word <= pos) {
234 if (word[0] == '\\') {
235 offset--;
236 word++;
238 word[offset] = word[0];
239 word++;
243 if (end) {
244 *_pos = pos;
245 return B_OK;
248 // Scan for next beginning word, open brackets, or comment start
250 pos++;
251 while (true) {
252 *_pos = pos;
253 if (!pos[0])
254 return B_NO_ERROR;
256 if (is_parameter_separator(pos[0])) {
257 // an open bracket '{' could follow after the first
258 // newline, but not later
259 if (newLine)
260 return B_NO_ERROR;
262 newLine = true;
263 } else if (pos[0] == '{' || pos[0] == '}' || pos[0] == '#')
264 return B_NO_ERROR;
265 else if (!isspace(pos[0]))
266 return newLine ? B_NO_ERROR : CONTINUE_PARAMETER;
268 pos++;
273 static status_t
274 parse_parameter(struct driver_parameter *parameter, char **_pos, int32 level)
276 char *pos = *_pos;
277 status_t status;
279 // initialize parameter first
280 memset(parameter, 0, sizeof(struct driver_parameter));
282 status = get_word(&pos, &parameter->name, NO_ASSIGNMENT, true);
283 if (status == CONTINUE_PARAMETER) {
284 while (status == CONTINUE_PARAMETER) {
285 char **newArray, *value = NULL;
286 status = get_word(&pos, &value, parameter->value_count == 0
287 ? ALLOW_ASSIGNMENT : IGNORE_ASSIGNMENT, false);
288 if (status < B_OK)
289 break;
291 // enlarge value array and save the value
293 newArray = (char**)realloc(parameter->values,
294 (parameter->value_count + 1) * sizeof(char *));
295 if (newArray == NULL)
296 return B_NO_MEMORY;
298 parameter->values = newArray;
299 parameter->values[parameter->value_count++] = value;
303 *_pos = pos;
304 return status;
308 static status_t
309 parse_parameters(struct driver_parameter **_parameters, int *_count,
310 char **_pos, int32 level)
312 if (level > MAX_SETTINGS_LEVEL)
313 return B_LINK_LIMIT;
315 while (true) {
316 struct driver_parameter parameter;
317 struct driver_parameter *newArray;
318 status_t status;
320 status = parse_parameter(&parameter, _pos, level);
321 if (status < B_OK)
322 return status;
324 if (status != NO_PARAMETER) {
325 driver_parameter *newParameter;
327 newArray = (driver_parameter*)realloc(*_parameters, (*_count + 1)
328 * sizeof(struct driver_parameter));
329 if (newArray == NULL)
330 return B_NO_MEMORY;
332 memcpy(&newArray[*_count], &parameter, sizeof(struct driver_parameter));
333 newParameter = &newArray[*_count];
335 *_parameters = newArray;
336 (*_count)++;
338 // check for level beginning and end
339 if (**_pos == '{') {
340 // if we go a level deeper, just start all over again...
341 (*_pos)++;
342 status = parse_parameters(&newParameter->parameters,
343 &newParameter->parameter_count, _pos, level + 1);
344 if (status < B_OK)
345 return status;
349 if ((**_pos == '}' && level > 0)
350 || (**_pos == '\0' && level == 0)) {
351 // take the closing bracket from the stack
352 (*_pos)++;
353 return B_OK;
356 // obviously, something has gone wrong
357 if (**_pos == '}' || **_pos == '\0')
358 return B_ERROR;
363 static status_t
364 parse_settings(settings_handle *handle)
366 char *text = handle->text;
368 memset(&handle->settings, 0, sizeof(struct driver_settings));
370 // empty settings are allowed
371 if (text == NULL)
372 return B_OK;
374 return parse_parameters(&handle->settings.parameters,
375 &handle->settings.parameter_count, &text, 0);
379 static void
380 free_parameter(struct driver_parameter *parameter)
382 int32 i;
383 for (i = parameter->parameter_count; i-- > 0;)
384 free_parameter(&parameter->parameters[i]);
386 free(parameter->parameters);
387 free(parameter->values);
391 static void
392 free_settings(settings_handle *handle)
394 int32 i;
395 for (i = handle->settings.parameter_count; i-- > 0;)
396 free_parameter(&handle->settings.parameters[i]);
398 free(handle->settings.parameters);
399 free(handle->text);
400 free(handle);
404 static settings_handle *
405 new_settings(char *buffer, const char *driverName)
407 settings_handle *handle = (settings_handle*)malloc(sizeof(settings_handle));
408 if (handle == NULL)
409 return NULL;
411 handle->magic = SETTINGS_MAGIC;
412 handle->text = buffer;
414 #ifdef _KERNEL_MODE
415 if (driverName != NULL) {
416 handle->ref_count = 1;
417 strlcpy(handle->name, driverName, sizeof(handle->name));
418 } else {
419 handle->ref_count = -1;
420 handle->name[0] = 0;
422 #endif
424 if (parse_settings(handle) == B_OK)
425 return handle;
427 free(handle);
428 return NULL;
432 static settings_handle *
433 load_driver_settings_from_file(int file, const char *driverName)
435 struct stat stat;
437 // Allocate a buffer and read the whole file into it.
438 // We will keep this buffer in memory, until the settings
439 // are unloaded.
440 // The driver_parameter::name field will point directly
441 // to this buffer.
443 if (fstat(file, &stat) < B_OK)
444 return NULL;
446 if (stat.st_size > B_OK && stat.st_size < MAX_SETTINGS_SIZE) {
447 char *text = (char *)malloc(stat.st_size + 1);
448 if (text != NULL && read(file, text, stat.st_size) == stat.st_size) {
449 settings_handle *handle;
451 text[stat.st_size] = '\0';
452 // make sure the string is null terminated
453 // to avoid misbehaviour
455 handle = new_settings(text, driverName);
456 if (handle != NULL) {
457 // everything went fine!
458 return handle;
461 free(handle);
463 // "text" might be NULL here, but that's allowed
464 free(text);
467 return NULL;
471 static bool
472 put_string(char **_buffer, ssize_t *_bufferSize, char *string)
474 size_t length, reserved, quotes;
475 char *buffer = *_buffer, c;
476 bool quoted;
478 if (string == NULL)
479 return true;
481 for (length = reserved = quotes = 0; (c = string[length]) != '\0'; length++) {
482 if (c == '"')
483 quotes++;
484 else if (is_word_break(c))
485 reserved++;
487 quoted = reserved || quotes;
489 // update _bufferSize in any way, so that we can chain several
490 // of these calls without having to check the return value
491 // everytime
492 *_bufferSize -= length + (quoted ? 2 + quotes : 0);
494 if (*_bufferSize <= 0)
495 return false;
497 if (quoted)
498 *(buffer++) = '"';
500 for (;(c = string[0]) != '\0'; string++) {
501 if (c == '"')
502 *(buffer++) = '\\';
504 *(buffer++) = c;
507 if (quoted)
508 *(buffer++) = '"';
510 buffer[0] = '\0';
512 // update the buffer position
513 *_buffer = buffer;
515 return true;
519 static bool
520 put_chars(char **_buffer, ssize_t *_bufferSize, const char *chars)
522 char *buffer = *_buffer;
523 size_t length;
525 if (chars == NULL)
526 return true;
528 length = strlen(chars);
529 *_bufferSize -= length;
531 if (*_bufferSize <= 0)
532 return false;
534 memcpy(buffer, chars, length);
535 buffer += length;
536 buffer[0] = '\0';
538 // update the buffer position
539 *_buffer = buffer;
541 return true;
545 static bool
546 put_char(char **_buffer, ssize_t *_bufferSize, char c)
548 char *buffer = *_buffer;
550 *_bufferSize -= 1;
552 if (*_bufferSize <= 0)
553 return false;
555 buffer[0] = c;
556 buffer[1] = '\0';
558 // update the buffer position
559 *_buffer = buffer + 1;
561 return true;
565 static void
566 put_level_space(char **_buffer, ssize_t *_bufferSize, int32 level)
568 while (level-- > 0)
569 put_char(_buffer, _bufferSize, '\t');
573 static void
574 put_parameter(char **_buffer, ssize_t *_bufferSize,
575 struct driver_parameter *parameter, int32 level, bool flat)
577 int32 i;
579 if (!flat)
580 put_level_space(_buffer, _bufferSize, level);
582 put_string(_buffer, _bufferSize, parameter->name);
583 if (flat && parameter->value_count > 0)
584 put_chars(_buffer, _bufferSize, " =");
586 for (i = 0; i < parameter->value_count; i++) {
587 put_char(_buffer, _bufferSize, ' ');
588 put_string(_buffer, _bufferSize, parameter->values[i]);
591 if (parameter->parameter_count > 0) {
592 put_chars(_buffer, _bufferSize, " {");
593 if (!flat)
594 put_char(_buffer, _bufferSize, '\n');
596 for (i = 0; i < parameter->parameter_count; i++) {
597 put_parameter(_buffer, _bufferSize, &parameter->parameters[i],
598 level + 1, flat);
600 if (parameter->parameters[i].parameter_count == 0)
601 put_chars(_buffer, _bufferSize, flat ? "; " : "\n");
604 if (!flat)
605 put_level_space(_buffer, _bufferSize, level);
606 put_chars(_buffer, _bufferSize, flat ? "}" : "}\n");
611 // #pragma mark - Kernel only functions
614 #ifdef _KERNEL_MODE
615 static settings_handle *
616 find_driver_settings(const char *name)
618 settings_handle *handle = NULL;
620 ASSERT_LOCKED_MUTEX(&sLock);
622 while ((handle = (settings_handle*)list_get_next_item(&sHandles, handle))
623 != NULL) {
624 if (!strcmp(handle->name, name))
625 return handle;
628 return NULL;
632 status_t
633 driver_settings_init(kernel_args *args)
635 struct driver_settings_file *settings = args->driver_settings;
637 // Move the preloaded driver settings over to the kernel
639 list_init(&sHandles);
641 while (settings != NULL) {
642 settings_handle *handle
643 = (settings_handle*)malloc(sizeof(settings_handle));
644 if (handle == NULL)
645 return B_NO_MEMORY;
647 if (settings->size != 0) {
648 handle->text = (char*)malloc(settings->size + 1);
649 if (handle->text == NULL) {
650 free(handle);
651 return B_NO_MEMORY;
654 memcpy(handle->text, settings->buffer, settings->size);
655 handle->text[settings->size] = '\0';
656 // null terminate the buffer
657 } else
658 handle->text = NULL;
660 strlcpy(handle->name, settings->name, sizeof(handle->name));
661 handle->settings.parameters = NULL;
662 handle->settings.parameter_count = 0;
663 handle->magic = 0;
664 // this triggers parsing the settings when they are actually used
666 if (!strcmp(handle->name, B_SAFEMODE_DRIVER_SETTINGS)) {
667 // These settings cannot be reloaded, so we better don't throw
668 // them away.
669 handle->ref_count = 1;
670 } else
671 handle->ref_count = 0;
673 list_add_item(&sHandles, handle);
675 settings = settings->next;
678 return B_OK;
680 #endif
683 // #pragma mark - public API
686 status_t
687 unload_driver_settings(void *_handle)
689 settings_handle *handle = (settings_handle *)_handle;
690 if (!check_handle(handle))
691 return B_BAD_VALUE;
693 #ifdef _KERNEL_MODE
694 mutex_lock(&sLock);
696 if (handle->ref_count > 0) {
697 if (--handle->ref_count == 0 && gBootDevice > 0) {
698 // don't unload an handle when /boot is not available
699 list_remove_link(&handle->link);
700 } else
701 handle = NULL;
703 mutex_unlock(&sLock);
704 #endif
706 if (handle != NULL)
707 free_settings(handle);
709 return B_OK;
713 void *
714 load_driver_settings(const char *driverName)
716 settings_handle *handle;
717 int file = -1;
719 if (driverName == NULL)
720 return NULL;
722 #ifdef _KERNEL_MODE
723 // see if we already have these settings loaded
724 mutex_lock(&sLock);
725 handle = find_driver_settings(driverName);
726 if (handle != NULL && handle->ref_count == 0 && gBootDevice > 0) {
727 // A handle with a zero ref_count should be unloaded if /boot is
728 // available.
729 list_remove_link(&handle->link);
730 free_settings(handle);
731 } else if (handle != NULL) {
732 handle->ref_count++;
734 // we got it, now let's see if it already has been parsed
735 if (handle->magic != SETTINGS_MAGIC) {
736 handle->magic = SETTINGS_MAGIC;
738 if (parse_settings(handle) != B_OK) {
739 // no valid settings, let's cut down its memory requirements
740 free(handle->text);
741 handle->text = NULL;
742 handle = NULL;
745 mutex_unlock(&sLock);
746 return handle;
749 // we are allowed to call the driver settings pretty early in the boot process
750 if (gKernelStartup) {
751 mutex_unlock(&sLock);
752 return NULL;
754 #endif // _KERNEL_MODE
755 #ifdef _BOOT_MODE
756 // see if we already have these settings loaded
758 struct driver_settings_file *settings = gKernelArgs.driver_settings;
759 while (settings != NULL) {
760 if (!strcmp(settings->name, driverName)) {
761 // we have it - since the buffer is clobbered, we have to
762 // copy its contents, though
763 char *text = (char*)malloc(settings->size + 1);
764 if (text == NULL)
765 return NULL;
767 memcpy(text, settings->buffer, settings->size + 1);
768 return new_settings(text, driverName);
770 settings = settings->next;
773 #endif // _BOOT_MODE
775 // open the settings from the standardized location
776 if (driverName[0] != '/') {
777 char path[B_FILE_NAME_LENGTH + 64];
779 #ifdef _BOOT_MODE
780 strcpy(path, kUserSettingsDirectory);
781 #else
782 // TODO: use B_SYSTEM_SETTINGS_DIRECTORY instead!
783 if (__find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, path,
784 sizeof(path)) == B_OK)
785 #endif
787 strlcat(path, SETTINGS_DIRECTORY, sizeof(path));
788 strlcat(path, driverName, sizeof(path));
791 file = open(path, O_RDONLY);
792 } else
793 file = open(driverName, O_RDONLY);
795 if (file < B_OK) {
796 #ifdef _KERNEL_MODE
797 mutex_unlock(&sLock);
798 #endif
799 return NULL;
802 handle = load_driver_settings_from_file(file, driverName);
804 #ifdef _KERNEL_MODE
805 if (handle != NULL)
806 list_add_item(&sHandles, handle);
807 mutex_unlock(&sLock);
808 #endif
810 close(file);
811 return (void *)handle;
815 void*
816 load_driver_settings_file(int fd)
818 return load_driver_settings_from_file(fd, NULL);
823 Returns a new driver_settings handle that has the parsed contents
824 of the passed string.
825 You can get an empty driver_settings object when you pass NULL as
826 the "settingsString" parameter.
828 void *
829 parse_driver_settings_string(const char *settingsString)
831 char *text = NULL;
832 if (settingsString != NULL) {
833 // we simply copy the whole string to use it as our internal buffer
834 text = strdup(settingsString);
835 if (text == NULL)
836 return NULL;
839 settings_handle *handle = new_settings(text, NULL);
840 if (handle == NULL)
841 free(text);
842 return handle;
847 This function prints out a driver settings structure to a human
848 readable string.
849 It's either in standard style or the single line style speficied
850 by the "flat" parameter.
851 If the buffer is too small to hold the string, B_BUFFER_OVERFLOW
852 is returned, and the needed amount of bytes if placed in the
853 "_bufferSize" parameter.
854 If the "handle" parameter is not a valid driver settings handle, or
855 the "buffer" parameter is NULL, B_BAD_VALUE is returned.
857 status_t
858 get_driver_settings_string(void *_handle, char *buffer, ssize_t *_bufferSize,
859 bool flat)
861 settings_handle *handle = (settings_handle *)_handle;
862 ssize_t bufferSize = *_bufferSize;
863 int32 i;
865 if (!check_handle(handle) || !buffer || *_bufferSize == 0)
866 return B_BAD_VALUE;
868 for (i = 0; i < handle->settings.parameter_count; i++) {
869 put_parameter(&buffer, &bufferSize, &handle->settings.parameters[i],
870 0, flat);
873 *_bufferSize -= bufferSize;
874 return bufferSize >= 0 ? B_OK : B_BUFFER_OVERFLOW;
879 Matches the first value of the parameter matching "keyName" with a set
880 of boolean values like 1/true/yes/on/enabled/...
881 Returns "unknownValue" if the parameter could not be found or doesn't
882 have any valid boolean setting, and "noArgValue" if the parameter
883 doesn't have any values.
884 Also returns "unknownValue" if the handle passed in was not valid.
886 bool
887 get_driver_boolean_parameter(void *_handle, const char *keyName,
888 bool unknownValue, bool noArgValue)
890 settings_handle *handle = (settings_handle*)_handle;
891 driver_parameter *parameter;
892 char *boolean;
894 if (!check_handle(handle))
895 return unknownValue;
897 // check for the parameter
898 if ((parameter = get_parameter(handle, keyName)) == NULL)
899 return unknownValue;
901 // check for the argument
902 if (parameter->value_count <= 0)
903 return noArgValue;
905 boolean = parameter->values[0];
906 if (!strcmp(boolean, "1")
907 || !strcasecmp(boolean, "true")
908 || !strcasecmp(boolean, "yes")
909 || !strcasecmp(boolean, "on")
910 || !strcasecmp(boolean, "enable")
911 || !strcasecmp(boolean, "enabled"))
912 return true;
914 if (!strcmp(boolean, "0")
915 || !strcasecmp(boolean, "false")
916 || !strcasecmp(boolean, "no")
917 || !strcasecmp(boolean, "off")
918 || !strcasecmp(boolean, "disable")
919 || !strcasecmp(boolean, "disabled"))
920 return false;
922 // if no known keyword is found, "unknownValue" is returned
923 return unknownValue;
927 const char *
928 get_driver_parameter(void *_handle, const char *keyName,
929 const char *unknownValue, const char *noArgValue)
931 settings_handle* handle = (settings_handle*)_handle;
932 struct driver_parameter *parameter;
934 if (!check_handle(handle))
935 return unknownValue;
937 // check for the parameter
938 if ((parameter = get_parameter(handle, keyName)) == NULL)
939 return unknownValue;
941 // check for the argument
942 if (parameter->value_count <= 0)
943 return noArgValue;
945 return parameter->values[0];
949 const driver_settings *
950 get_driver_settings(void *handle)
952 if (!check_handle((settings_handle*)handle))
953 return NULL;
955 return &((settings_handle *)handle)->settings;
959 status_t
960 delete_driver_settings(void *_handle)
962 return unload_driver_settings(_handle);