source: mainline/generic/src/console/kconsole.c@ 59b6a70

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

Improve kconsole's support for recognition of commands with arguments.
Implement ARG_TYPE_STRING.
Add 'describe' command.
Move kconsole.c to generic/src/console.
Move kconsole.h to generic/include/console.

  • Property mode set to 100644
File size: 9.8 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#include <console/kconsole.h>
30#include <console/console.h>
31#include <console/chardev.h>
32#include <print.h>
33#include <panic.h>
34#include <typedefs.h>
35#include <arch/types.h>
36#include <list.h>
37#include <arch.h>
38#include <func.h>
39#include <macros.h>
40#include <debug.h>
41
42#define MAX_CMDLINE 256
43
44/** Simple kernel console.
45 *
46 * The console is realized by kernel thread kconsole.
47 * It doesn't understand any useful command on its own,
48 * but makes it possible for other kernel subsystems to
49 * register their own commands.
50 */
51
52/** Locking.
53 *
54 * There is a list of cmd_info_t structures. This list
55 * is protected by cmd_lock spinlock. Note that specially
56 * the link elements of cmd_info_t are protected by
57 * this lock.
58 *
59 * Each cmd_info_t also has its own lock, which protects
60 * all elements thereof except the link element.
61 *
62 * cmd_lock must be acquired before any cmd_info lock.
63 * When locking two cmd info structures, structure with
64 * lower address must be locked first.
65 */
66
67spinlock_t cmd_lock; /**< Lock protecting command list. */
68link_t cmd_head; /**< Command list. */
69
70static cmd_info_t *parse_cmdline(char *cmdline, size_t len);
71static bool parse_argument(char *cmdline, size_t len, index_t *start, index_t *end);
72
73/** Data and methods for 'help' command. */
74static int cmd_help(cmd_arg_t *argv);
75static cmd_info_t help_info;
76
77/** Data and methods for 'description' command. */
78static int cmd_desc(cmd_arg_t *argv);
79static void desc_help(void);
80static cmd_info_t desc_info;
81static char desc_buf[MAX_CMDLINE+1];
82static cmd_arg_t desc_argv = {
83 .type = ARG_TYPE_STRING,
84 .buffer = desc_buf,
85 .len = sizeof(desc_buf)
86};
87
88/** Initialize kconsole data structures. */
89void kconsole_init(void)
90{
91 spinlock_initialize(&cmd_lock);
92 list_initialize(&cmd_head);
93
94 help_info.name = "help";
95 help_info.description = "List supported commands.";
96 help_info.func = cmd_help;
97 help_info.help = NULL;
98 help_info.argc = 0;
99 help_info.argv = NULL;
100
101 spinlock_initialize(&help_info.lock);
102 link_initialize(&help_info.link);
103
104 if (!cmd_register(&help_info))
105 panic("could not register command %s\n", help_info.name);
106
107
108 desc_info.name = "describe";
109 desc_info.description = "Describe specified command.";
110 desc_info.help = desc_help;
111 desc_info.func = cmd_desc;
112 desc_info.argc = 1;
113 desc_info.argv = &desc_argv;
114
115 spinlock_initialize(&desc_info.lock);
116 link_initialize(&desc_info.link);
117
118 if (!cmd_register(&desc_info))
119 panic("could not register command %s\n", desc_info.name);
120}
121
122
123/** Register kconsole command.
124 *
125 * @param cmd Structure describing the command.
126 *
127 * @return 0 on failure, 1 on success.
128 */
129int cmd_register(cmd_info_t *cmd)
130{
131 ipl_t ipl;
132 link_t *cur;
133
134 ipl = interrupts_disable();
135 spinlock_lock(&cmd_lock);
136
137 /*
138 * Make sure the command is not already listed.
139 */
140 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
141 cmd_info_t *hlp;
142
143 hlp = list_get_instance(cur, cmd_info_t, link);
144
145 if (hlp == cmd) {
146 /* The command is already there. */
147 spinlock_unlock(&cmd_lock);
148 interrupts_restore(ipl);
149 return 0;
150 }
151
152 /* Avoid deadlock. */
153 if (hlp < cmd) {
154 spinlock_lock(&hlp->lock);
155 spinlock_lock(&cmd->lock);
156 } else {
157 spinlock_lock(&cmd->lock);
158 spinlock_lock(&hlp->lock);
159 }
160
161 if ((strncmp(hlp->name, cmd->name, strlen(cmd->name)) == 0)) {
162 /* The command is already there. */
163 spinlock_unlock(&hlp->lock);
164 spinlock_unlock(&cmd->lock);
165 spinlock_unlock(&cmd_lock);
166 interrupts_restore(ipl);
167 return 0;
168 }
169
170 spinlock_unlock(&hlp->lock);
171 spinlock_unlock(&cmd->lock);
172 }
173
174 /*
175 * Now the command can be added.
176 */
177 list_append(&cmd->link, &cmd_head);
178
179 spinlock_unlock(&cmd_lock);
180 interrupts_restore(ipl);
181 return 1;
182}
183
184/** Kernel console managing thread.
185 *
186 * @param arg Not used.
187 */
188void kconsole(void *arg)
189{
190 char cmdline[MAX_CMDLINE+1];
191 cmd_info_t *cmd_info;
192 count_t len;
193
194 if (!stdin) {
195 printf("%s: no stdin\n", __FUNCTION__);
196 return;
197 }
198
199 while (true) {
200 printf("%s> ", __FUNCTION__);
201 if (!(len = gets(stdin, cmdline, sizeof(cmdline))))
202 continue;
203 cmdline[len] = '\0';
204 cmd_info = parse_cmdline(cmdline, len);
205 if (!cmd_info)
206 continue;
207 (void) cmd_info->func(cmd_info->argv);
208 }
209}
210
211/** Parse command line.
212 *
213 * @param cmdline Command line as read from input device.
214 * @param len Command line length.
215 *
216 * @return Structure describing the command.
217 */
218cmd_info_t *parse_cmdline(char *cmdline, size_t len)
219{
220 index_t start = 0, end = 0;
221 cmd_info_t *cmd = NULL;
222 link_t *cur;
223 ipl_t ipl;
224 int i;
225
226 if (!parse_argument(cmdline, len, &start, &end)) {
227 /* Command line did not contain alphanumeric word. */
228 return NULL;
229 }
230
231 ipl = interrupts_disable();
232 spinlock_lock(&cmd_lock);
233
234 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
235 cmd_info_t *hlp;
236
237 hlp = list_get_instance(cur, cmd_info_t, link);
238 spinlock_lock(&hlp->lock);
239
240 if (strncmp(hlp->name, &cmdline[start], (end - start) + 1) == 0) {
241 cmd = hlp;
242 break;
243 }
244
245 spinlock_unlock(&hlp->lock);
246 }
247
248 spinlock_unlock(&cmd_lock);
249
250 if (!cmd) {
251 /* Unknown command. */
252 printf("Unknown command.\n");
253 interrupts_restore(ipl);
254 return NULL;
255 }
256
257 /* cmd == hlp is locked */
258
259 /*
260 * The command line must be further analyzed and
261 * the parameters therefrom must be matched and
262 * converted to those specified in the cmd info
263 * structure.
264 */
265
266 for (i = 0; i < cmd->argc; i++) {
267 char *buf;
268 start = end + 1;
269 if (!parse_argument(cmdline, len, &start, &end)) {
270 printf("Too few arguments.\n");
271 spinlock_unlock(&cmd->lock);
272 interrupts_restore(ipl);
273 return NULL;
274 }
275
276 switch (cmd->argv[i].type) {
277 case ARG_TYPE_STRING:
278 buf = cmd->argv[i].buffer;
279 strncpy(buf, (const char *) &cmdline[start], min((end - start) + 1, cmd->argv[i].len - 1));
280 buf[min((end - start) + 1, cmd->argv[i].len - 1)] = '\0';
281 break;
282 case ARG_TYPE_INT:
283 case ARG_TYPE_INVALID:
284 default:
285 panic("invalid argument type\n");
286 break;
287 }
288 }
289
290 start = end + 1;
291 if (parse_argument(cmdline, len, &start, &end)) {
292 printf("Too many arguments.\n");
293 spinlock_unlock(&cmd->lock);
294 interrupts_restore(ipl);
295 return NULL;
296 }
297
298 spinlock_unlock(&cmd->lock);
299 interrupts_restore(ipl);
300 return cmd;
301}
302
303/** Parse argument.
304 *
305 * Find start and end positions of command line argument.
306 *
307 * @param cmdline Command line as read from the input device.
308 * @param len Number of characters in cmdline.
309 * @param start On entry, 'start' contains pointer to the index
310 * of first unprocessed character of cmdline.
311 * On successful exit, it marks beginning of the next argument.
312 * @param end Undefined on entry. On exit, 'end' points to the last character
313 * of the next argument.
314 *
315 * @return false on failure, true on success.
316 */
317bool parse_argument(char *cmdline, size_t len, index_t *start, index_t *end)
318{
319 int i;
320 bool found_start = false;
321
322 ASSERT(start != NULL);
323 ASSERT(end != NULL);
324
325 for (i = *start; i < len; i++) {
326 if (!found_start) {
327 if (is_white(cmdline[i]))
328 (*start)++;
329 else
330 found_start = true;
331 } else {
332 if (is_white(cmdline[i]))
333 break;
334 }
335 }
336 *end = i - 1;
337
338 return found_start;
339}
340
341
342/** List supported commands.
343 *
344 * @param argv Argument vector.
345 *
346 * @return 0 on failure, 1 on success.
347 */
348int cmd_help(cmd_arg_t *argv)
349{
350 link_t *cur;
351 ipl_t ipl;
352
353 ipl = interrupts_disable();
354 spinlock_lock(&cmd_lock);
355
356 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
357 cmd_info_t *hlp;
358
359 hlp = list_get_instance(cur, cmd_info_t, link);
360 spinlock_lock(&hlp->lock);
361
362 printf("%s - %s\n", hlp->name, hlp->description);
363
364 spinlock_unlock(&hlp->lock);
365 }
366
367 spinlock_unlock(&cmd_lock);
368 interrupts_restore(ipl);
369
370 return 1;
371}
372
373/** Describe specified command.
374 *
375 * @param argv Argument vector.
376 *
377 * @return 0 on failure, 1 on success.
378 */
379int cmd_desc(cmd_arg_t *argv)
380{
381 link_t *cur;
382 ipl_t ipl;
383
384 ipl = interrupts_disable();
385 spinlock_lock(&cmd_lock);
386
387 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
388 cmd_info_t *hlp;
389
390 hlp = list_get_instance(cur, cmd_info_t, link);
391 spinlock_lock(&hlp->lock);
392
393 if (strncmp(hlp->name, (const char *) argv->buffer, strlen(hlp->name)) == 0) {
394 printf("%s - %s\n", hlp->name, hlp->description);
395 if (hlp->help)
396 hlp->help();
397 spinlock_unlock(&hlp->lock);
398 break;
399 }
400
401 spinlock_unlock(&hlp->lock);
402 }
403
404 spinlock_unlock(&cmd_lock);
405 interrupts_restore(ipl);
406
407 return 1;
408}
409
410/** Print detailed description of 'describe' command. */
411void desc_help(void)
412{
413 printf("Syntax: describe command_name\n");
414}
Note: See TracBrowser for help on using the repository browser.