source: mainline/kernel/generic/src/console/cmd.c@ f5e39a32

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f5e39a32 was 2b017ba, checked in by Jakub Jermar <jakub@…>, 19 years ago

Replace the old IRQ dispatcher and IPC notifier with new implementation.
Note that all architectures except for sparc64 are now broken
and don't even compile.

  • Property mode set to 100644
File size: 17.1 KB
Line 
1/*
2 * Copyright (C) 2005 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genericconsole
30 * @{
31 */
32
33/**
34 * @file cmd.c
35 * @brief Kernel console command wrappers.
36 *
37 * This file is meant to contain all wrapper functions for
38 * all kconsole commands. The point is in separating
39 * kconsole specific wrappers from kconsole-unaware functions
40 * from other subsystems.
41 */
42
43#include <console/cmd.h>
44#include <console/console.h>
45#include <console/kconsole.h>
46#include <print.h>
47#include <panic.h>
48#include <typedefs.h>
49#include <arch/types.h>
50#include <adt/list.h>
51#include <arch.h>
52#include <func.h>
53#include <macros.h>
54#include <debug.h>
55#include <symtab.h>
56#include <cpu.h>
57#include <mm/tlb.h>
58#include <arch/mm/tlb.h>
59#include <mm/frame.h>
60#include <main/version.h>
61#include <mm/slab.h>
62#include <proc/scheduler.h>
63#include <proc/thread.h>
64#include <proc/task.h>
65#include <ipc/ipc.h>
66#include <ipc/irq.h>
67
68/* Data and methods for 'help' command. */
69static int cmd_help(cmd_arg_t *argv);
70static cmd_info_t help_info = {
71 .name = "help",
72 .description = "List of supported commands.",
73 .func = cmd_help,
74 .argc = 0
75};
76
77static cmd_info_t exit_info = {
78 .name = "exit",
79 .description ="Exit kconsole",
80 .argc = 0
81};
82
83static int cmd_continue(cmd_arg_t *argv);
84static cmd_info_t continue_info = {
85 .name = "continue",
86 .description ="Return console back to userspace.",
87 .func = cmd_continue,
88 .argc = 0
89};
90
91/* Data and methods for 'description' command. */
92static int cmd_desc(cmd_arg_t *argv);
93static void desc_help(void);
94static char desc_buf[MAX_CMDLINE+1];
95static cmd_arg_t desc_argv = {
96 .type = ARG_TYPE_STRING,
97 .buffer = desc_buf,
98 .len = sizeof(desc_buf)
99};
100static cmd_info_t desc_info = {
101 .name = "describe",
102 .description = "Describe specified command.",
103 .help = desc_help,
104 .func = cmd_desc,
105 .argc = 1,
106 .argv = &desc_argv
107};
108
109/* Data and methods for 'symaddr' command. */
110static int cmd_symaddr(cmd_arg_t *argv);
111static char symaddr_buf[MAX_CMDLINE+1];
112static cmd_arg_t symaddr_argv = {
113 .type = ARG_TYPE_STRING,
114 .buffer = symaddr_buf,
115 .len = sizeof(symaddr_buf)
116};
117static cmd_info_t symaddr_info = {
118 .name = "symaddr",
119 .description = "Return symbol address.",
120 .func = cmd_symaddr,
121 .argc = 1,
122 .argv = &symaddr_argv
123};
124
125static char set_buf[MAX_CMDLINE+1];
126static int cmd_set4(cmd_arg_t *argv);
127static cmd_arg_t set4_argv[] = {
128 {
129 .type = ARG_TYPE_STRING,
130 .buffer = set_buf,
131 .len = sizeof(set_buf)
132 },
133 {
134 .type = ARG_TYPE_INT
135 }
136};
137static cmd_info_t set4_info = {
138 .name = "set4",
139 .description = "set <dest_addr> <value> - 4byte version",
140 .func = cmd_set4,
141 .argc = 2,
142 .argv = set4_argv
143};
144
145/* Data and methods for 'call0' command. */
146static char call0_buf[MAX_CMDLINE+1];
147static char carg1_buf[MAX_CMDLINE+1];
148static char carg2_buf[MAX_CMDLINE+1];
149static char carg3_buf[MAX_CMDLINE+1];
150
151static int cmd_call0(cmd_arg_t *argv);
152static cmd_arg_t call0_argv = {
153 .type = ARG_TYPE_STRING,
154 .buffer = call0_buf,
155 .len = sizeof(call0_buf)
156};
157static cmd_info_t call0_info = {
158 .name = "call0",
159 .description = "call0 <function> -> call function().",
160 .func = cmd_call0,
161 .argc = 1,
162 .argv = &call0_argv
163};
164
165/* Data and methods for 'call1' command. */
166static int cmd_call1(cmd_arg_t *argv);
167static cmd_arg_t call1_argv[] = {
168 {
169 .type = ARG_TYPE_STRING,
170 .buffer = call0_buf,
171 .len = sizeof(call0_buf)
172 },
173 {
174 .type = ARG_TYPE_VAR,
175 .buffer = carg1_buf,
176 .len = sizeof(carg1_buf)
177 }
178};
179static cmd_info_t call1_info = {
180 .name = "call1",
181 .description = "call1 <function> <arg1> -> call function(arg1).",
182 .func = cmd_call1,
183 .argc = 2,
184 .argv = call1_argv
185};
186
187/* Data and methods for 'call2' command. */
188static int cmd_call2(cmd_arg_t *argv);
189static cmd_arg_t call2_argv[] = {
190 {
191 .type = ARG_TYPE_STRING,
192 .buffer = call0_buf,
193 .len = sizeof(call0_buf)
194 },
195 {
196 .type = ARG_TYPE_VAR,
197 .buffer = carg1_buf,
198 .len = sizeof(carg1_buf)
199 },
200 {
201 .type = ARG_TYPE_VAR,
202 .buffer = carg2_buf,
203 .len = sizeof(carg2_buf)
204 }
205};
206static cmd_info_t call2_info = {
207 .name = "call2",
208 .description = "call2 <function> <arg1> <arg2> -> call function(arg1,arg2).",
209 .func = cmd_call2,
210 .argc = 3,
211 .argv = call2_argv
212};
213
214/* Data and methods for 'call3' command. */
215static int cmd_call3(cmd_arg_t *argv);
216static cmd_arg_t call3_argv[] = {
217 {
218 .type = ARG_TYPE_STRING,
219 .buffer = call0_buf,
220 .len = sizeof(call0_buf)
221 },
222 {
223 .type = ARG_TYPE_VAR,
224 .buffer = carg1_buf,
225 .len = sizeof(carg1_buf)
226 },
227 {
228 .type = ARG_TYPE_VAR,
229 .buffer = carg2_buf,
230 .len = sizeof(carg2_buf)
231 },
232 {
233 .type = ARG_TYPE_VAR,
234 .buffer = carg3_buf,
235 .len = sizeof(carg3_buf)
236 }
237
238};
239static cmd_info_t call3_info = {
240 .name = "call3",
241 .description = "call3 <function> <arg1> <arg2> <arg3> -> call function(arg1,arg2,arg3).",
242 .func = cmd_call3,
243 .argc = 4,
244 .argv = call3_argv
245};
246
247/* Data and methods for 'halt' command. */
248static int cmd_halt(cmd_arg_t *argv);
249static cmd_info_t halt_info = {
250 .name = "halt",
251 .description = "Halt the kernel.",
252 .func = cmd_halt,
253 .argc = 0
254};
255
256/* Data and methods for 'tlb' command. */
257static int cmd_tlb(cmd_arg_t *argv);
258cmd_info_t tlb_info = {
259 .name = "tlb",
260 .description = "Print TLB of current processor.",
261 .help = NULL,
262 .func = cmd_tlb,
263 .argc = 0,
264 .argv = NULL
265};
266
267static int cmd_threads(cmd_arg_t *argv);
268static cmd_info_t threads_info = {
269 .name = "threads",
270 .description = "List all threads.",
271 .func = cmd_threads,
272 .argc = 0
273};
274
275static int cmd_tasks(cmd_arg_t *argv);
276static cmd_info_t tasks_info = {
277 .name = "tasks",
278 .description = "List all tasks.",
279 .func = cmd_tasks,
280 .argc = 0
281};
282
283
284static int cmd_sched(cmd_arg_t *argv);
285static cmd_info_t sched_info = {
286 .name = "scheduler",
287 .description = "List all scheduler information.",
288 .func = cmd_sched,
289 .argc = 0
290};
291
292static int cmd_slabs(cmd_arg_t *argv);
293static cmd_info_t slabs_info = {
294 .name = "slabs",
295 .description = "List slab caches.",
296 .func = cmd_slabs,
297 .argc = 0
298};
299
300/* Data and methods for 'zones' command */
301static int cmd_zones(cmd_arg_t *argv);
302static cmd_info_t zones_info = {
303 .name = "zones",
304 .description = "List of memory zones.",
305 .func = cmd_zones,
306 .argc = 0
307};
308
309/* Data and methods for 'ipc_task' command */
310static int cmd_ipc_task(cmd_arg_t *argv);
311static cmd_arg_t ipc_task_argv = {
312 .type = ARG_TYPE_INT,
313};
314static cmd_info_t ipc_task_info = {
315 .name = "ipc_task",
316 .description = "ipc_task <taskid> Show IPC information of given task.",
317 .func = cmd_ipc_task,
318 .argc = 1,
319 .argv = &ipc_task_argv
320};
321
322/* Data and methods for 'zone' command */
323static int cmd_zone(cmd_arg_t *argv);
324static cmd_arg_t zone_argv = {
325 .type = ARG_TYPE_INT,
326};
327
328static cmd_info_t zone_info = {
329 .name = "zone",
330 .description = "Show memory zone structure.",
331 .func = cmd_zone,
332 .argc = 1,
333 .argv = &zone_argv
334};
335
336/* Data and methods for 'cpus' command. */
337static int cmd_cpus(cmd_arg_t *argv);
338cmd_info_t cpus_info = {
339 .name = "cpus",
340 .description = "List all processors.",
341 .help = NULL,
342 .func = cmd_cpus,
343 .argc = 0,
344 .argv = NULL
345};
346
347/* Data and methods for 'version' command. */
348static int cmd_version(cmd_arg_t *argv);
349cmd_info_t version_info = {
350 .name = "version",
351 .description = "Print version information.",
352 .help = NULL,
353 .func = cmd_version,
354 .argc = 0,
355 .argv = NULL
356};
357
358static cmd_info_t *basic_commands[] = {
359 &call0_info,
360 &call1_info,
361 &call2_info,
362 &call3_info,
363 &continue_info,
364 &cpus_info,
365 &desc_info,
366 &exit_info,
367 &halt_info,
368 &help_info,
369 &ipc_task_info,
370 &set4_info,
371 &slabs_info,
372 &symaddr_info,
373 &sched_info,
374 &threads_info,
375 &tasks_info,
376 &tlb_info,
377 &version_info,
378 &zones_info,
379 &zone_info,
380 NULL
381};
382
383
384/** Initialize command info structure.
385 *
386 * @param cmd Command info structure.
387 *
388 */
389void cmd_initialize(cmd_info_t *cmd)
390{
391 spinlock_initialize(&cmd->lock, "cmd");
392 link_initialize(&cmd->link);
393}
394
395/** Initialize and register commands. */
396void cmd_init(void)
397{
398 int i;
399
400 for (i=0;basic_commands[i]; i++) {
401 cmd_initialize(basic_commands[i]);
402 if (!cmd_register(basic_commands[i]))
403 panic("could not register command %s\n",
404 basic_commands[i]->name);
405 }
406}
407
408
409/** List supported commands.
410 *
411 * @param argv Argument vector.
412 *
413 * @return 0 on failure, 1 on success.
414 */
415int cmd_help(cmd_arg_t *argv)
416{
417 link_t *cur;
418
419 spinlock_lock(&cmd_lock);
420
421 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
422 cmd_info_t *hlp;
423
424 hlp = list_get_instance(cur, cmd_info_t, link);
425 spinlock_lock(&hlp->lock);
426
427 printf("%s - %s\n", hlp->name, hlp->description);
428
429 spinlock_unlock(&hlp->lock);
430 }
431
432 spinlock_unlock(&cmd_lock);
433
434 return 1;
435}
436
437/** Describe specified command.
438 *
439 * @param argv Argument vector.
440 *
441 * @return 0 on failure, 1 on success.
442 */
443int cmd_desc(cmd_arg_t *argv)
444{
445 link_t *cur;
446
447 spinlock_lock(&cmd_lock);
448
449 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
450 cmd_info_t *hlp;
451
452 hlp = list_get_instance(cur, cmd_info_t, link);
453 spinlock_lock(&hlp->lock);
454
455 if (strncmp(hlp->name, (const char *) argv->buffer, strlen(hlp->name)) == 0) {
456 printf("%s - %s\n", hlp->name, hlp->description);
457 if (hlp->help)
458 hlp->help();
459 spinlock_unlock(&hlp->lock);
460 break;
461 }
462
463 spinlock_unlock(&hlp->lock);
464 }
465
466 spinlock_unlock(&cmd_lock);
467
468 return 1;
469}
470
471/** Search symbol table */
472int cmd_symaddr(cmd_arg_t *argv)
473{
474 symtab_print_search(argv->buffer);
475
476 return 1;
477}
478
479/** Call function with zero parameters */
480int cmd_call0(cmd_arg_t *argv)
481{
482 uintptr_t symaddr;
483 char *symbol;
484 unative_t (*f)(void);
485#ifdef ia64
486 struct {
487 unative_t f;
488 unative_t gp;
489 }fptr;
490#endif
491
492 symaddr = get_symbol_addr(argv->buffer);
493 if (!symaddr)
494 printf("Symbol %s not found.\n", argv->buffer);
495 else if (symaddr == (uintptr_t) -1) {
496 symtab_print_search(argv->buffer);
497 printf("Duplicate symbol, be more specific.\n");
498 } else {
499 symbol = get_symtab_entry(symaddr);
500 printf("Calling f(): %.*p: %s\n", sizeof(uintptr_t) * 2, symaddr, symbol);
501#ifdef ia64
502 fptr.f = symaddr;
503 fptr.gp = ((unative_t *)cmd_call2)[1];
504 f = (unative_t (*)(void)) &fptr;
505#else
506 f = (unative_t (*)(void)) symaddr;
507#endif
508 printf("Result: %#zx\n", f());
509 }
510
511 return 1;
512}
513
514/** Call function with one parameter */
515int cmd_call1(cmd_arg_t *argv)
516{
517 uintptr_t symaddr;
518 char *symbol;
519 unative_t (*f)(unative_t,...);
520 unative_t arg1 = argv[1].intval;
521#ifdef ia64
522 struct {
523 unative_t f;
524 unative_t gp;
525 }fptr;
526#endif
527
528 symaddr = get_symbol_addr(argv->buffer);
529 if (!symaddr)
530 printf("Symbol %s not found.\n", argv->buffer);
531 else if (symaddr == (uintptr_t) -1) {
532 symtab_print_search(argv->buffer);
533 printf("Duplicate symbol, be more specific.\n");
534 } else {
535 symbol = get_symtab_entry(symaddr);
536
537 printf("Calling f(%#zx): %.*p: %s\n", arg1, sizeof(uintptr_t) * 2, symaddr, symbol);
538#ifdef ia64
539 fptr.f = symaddr;
540 fptr.gp = ((unative_t *)cmd_call2)[1];
541 f = (unative_t (*)(unative_t,...)) &fptr;
542#else
543 f = (unative_t (*)(unative_t,...)) symaddr;
544#endif
545 printf("Result: %#zx\n", f(arg1));
546 }
547
548 return 1;
549}
550
551/** Call function with two parameters */
552int cmd_call2(cmd_arg_t *argv)
553{
554 uintptr_t symaddr;
555 char *symbol;
556 unative_t (*f)(unative_t,unative_t,...);
557 unative_t arg1 = argv[1].intval;
558 unative_t arg2 = argv[2].intval;
559#ifdef ia64
560 struct {
561 unative_t f;
562 unative_t gp;
563 }fptr;
564#endif
565
566 symaddr = get_symbol_addr(argv->buffer);
567 if (!symaddr)
568 printf("Symbol %s not found.\n", argv->buffer);
569 else if (symaddr == (uintptr_t) -1) {
570 symtab_print_search(argv->buffer);
571 printf("Duplicate symbol, be more specific.\n");
572 } else {
573 symbol = get_symtab_entry(symaddr);
574 printf("Calling f(0x%zx,0x%zx): %.*p: %s\n",
575 arg1, arg2, sizeof(uintptr_t) * 2, symaddr, symbol);
576#ifdef ia64
577 fptr.f = symaddr;
578 fptr.gp = ((unative_t *)cmd_call2)[1];
579 f = (unative_t (*)(unative_t,unative_t,...)) &fptr;
580#else
581 f = (unative_t (*)(unative_t,unative_t,...)) symaddr;
582#endif
583 printf("Result: %#zx\n", f(arg1, arg2));
584 }
585
586 return 1;
587}
588
589/** Call function with three parameters */
590int cmd_call3(cmd_arg_t *argv)
591{
592 uintptr_t symaddr;
593 char *symbol;
594 unative_t (*f)(unative_t,unative_t,unative_t,...);
595 unative_t arg1 = argv[1].intval;
596 unative_t arg2 = argv[2].intval;
597 unative_t arg3 = argv[3].intval;
598#ifdef ia64
599 struct {
600 unative_t f;
601 unative_t gp;
602 }fptr;
603#endif
604
605 symaddr = get_symbol_addr(argv->buffer);
606 if (!symaddr)
607 printf("Symbol %s not found.\n", argv->buffer);
608 else if (symaddr == (uintptr_t) -1) {
609 symtab_print_search(argv->buffer);
610 printf("Duplicate symbol, be more specific.\n");
611 } else {
612 symbol = get_symtab_entry(symaddr);
613 printf("Calling f(0x%zx,0x%zx, 0x%zx): %.*p: %s\n",
614 arg1, arg2, arg3, sizeof(uintptr_t) * 2, symaddr, symbol);
615#ifdef ia64
616 fptr.f = symaddr;
617 fptr.gp = ((unative_t *)cmd_call2)[1];
618 f = (unative_t (*)(unative_t,unative_t,unative_t,...)) &fptr;
619#else
620 f = (unative_t (*)(unative_t,unative_t,unative_t,...)) symaddr;
621#endif
622 printf("Result: %#zx\n", f(arg1, arg2, arg3));
623 }
624
625 return 1;
626}
627
628
629/** Print detailed description of 'describe' command. */
630void desc_help(void)
631{
632 printf("Syntax: describe command_name\n");
633}
634
635/** Halt the kernel.
636 *
637 * @param argv Argument vector (ignored).
638 *
639 * @return 0 on failure, 1 on success (never returns).
640 */
641int cmd_halt(cmd_arg_t *argv)
642{
643 halt();
644 return 1;
645}
646
647/** Command for printing TLB contents.
648 *
649 * @param argv Not used.
650 *
651 * @return Always returns 1.
652 */
653int cmd_tlb(cmd_arg_t *argv)
654{
655 tlb_print();
656 return 1;
657}
658
659/** Write 4 byte value to address */
660int cmd_set4(cmd_arg_t *argv)
661{
662 uint32_t *addr ;
663 uint32_t arg1 = argv[1].intval;
664 bool pointer = false;
665
666 if (((char *)argv->buffer)[0] == '*') {
667 addr = (uint32_t *) get_symbol_addr(argv->buffer+1);
668 pointer = true;
669 } else if (((char *)argv->buffer)[0] >= '0' &&
670 ((char *)argv->buffer)[0] <= '9')
671 addr = (uint32_t *)atoi((char *)argv->buffer);
672 else
673 addr = (uint32_t *)get_symbol_addr(argv->buffer);
674
675 if (!addr)
676 printf("Symbol %s not found.\n", argv->buffer);
677 else if (addr == (uint32_t *) -1) {
678 symtab_print_search(argv->buffer);
679 printf("Duplicate symbol, be more specific.\n");
680 } else {
681 if (pointer)
682 addr = (uint32_t *)(*(unative_t *)addr);
683 printf("Writing 0x%x -> %.*p\n", arg1, sizeof(uintptr_t) * 2, addr);
684 *addr = arg1;
685
686 }
687
688 return 1;
689}
690
691/** Command for listings SLAB caches
692 *
693 * @param argv Ignores
694 *
695 * @return Always 1
696 */
697int cmd_slabs(cmd_arg_t * argv) {
698 slab_print_list();
699 return 1;
700}
701
702
703/** Command for listings Thread information
704 *
705 * @param argv Ignores
706 *
707 * @return Always 1
708 */
709int cmd_threads(cmd_arg_t * argv) {
710 thread_print_list();
711 return 1;
712}
713
714/** Command for listings Task information
715 *
716 * @param argv Ignores
717 *
718 * @return Always 1
719 */
720int cmd_tasks(cmd_arg_t * argv) {
721 task_print_list();
722 return 1;
723}
724
725/** Command for listings Thread information
726 *
727 * @param argv Ignores
728 *
729 * @return Always 1
730 */
731int cmd_sched(cmd_arg_t * argv) {
732 sched_print_list();
733 return 1;
734}
735
736/** Command for listing memory zones
737 *
738 * @param argv Ignored
739 *
740 * return Always 1
741 */
742int cmd_zones(cmd_arg_t * argv) {
743 zone_print_list();
744 return 1;
745}
746
747/** Command for memory zone details
748 *
749 * @param argv Integer argument from cmdline expected
750 *
751 * return Always 1
752 */
753int cmd_zone(cmd_arg_t * argv) {
754 zone_print_one(argv[0].intval);
755 return 1;
756}
757
758/** Command for printing task ipc details
759 *
760 * @param argv Integer argument from cmdline expected
761 *
762 * return Always 1
763 */
764int cmd_ipc_task(cmd_arg_t * argv) {
765 ipc_print_task(argv[0].intval);
766 return 1;
767}
768
769
770/** Command for listing processors.
771 *
772 * @param argv Ignored.
773 *
774 * return Always 1.
775 */
776int cmd_cpus(cmd_arg_t *argv)
777{
778 cpu_list();
779 return 1;
780}
781
782/** Command for printing kernel version.
783 *
784 * @param argv Ignored.
785 *
786 * return Always 1.
787 */
788int cmd_version(cmd_arg_t *argv)
789{
790 version_print();
791 return 1;
792}
793
794/** Command for returning console back to userspace.
795 *
796 * @param argv Ignored.
797 *
798 * return Always 1.
799 */
800int cmd_continue(cmd_arg_t *argv)
801{
802 printf("The kernel will now relinquish the console.\n");
803 printf("Use userspace controls to redraw the screen.\n");
804 arch_release_console();
805 /* TODO: send some vitual IRQ */
806 ipc_irq_send_msg(NULL, 0, 0, 0);
807 return 1;
808}
809
810/** @}
811 */
Note: See TracBrowser for help on using the repository browser.