xref: /haiku/src/add-ons/kernel/drivers/common/console.cpp (revision 072d3935c2497638e9c2502f574c133caeba9d3d)
1 /*
2  * Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <Drivers.h>
11 #include <KernelExport.h>
12 
13 #include <console.h>
14 #include <lock.h>
15 
16 #include <string.h>
17 #include <stdio.h>
18 #include <termios.h>
19 
20 
21 #define DEVICE_NAME "console"
22 
23 #define TAB_SIZE 8
24 #define TAB_MASK 7
25 
26 #define FMASK 0x0f
27 #define BMASK 0x70
28 
29 typedef enum {
30 	CONSOLE_STATE_NORMAL = 0,
31 	CONSOLE_STATE_GOT_ESCAPE,
32 	CONSOLE_STATE_SEEN_BRACKET,
33 	CONSOLE_STATE_NEW_ARG,
34 	CONSOLE_STATE_PARSING_ARG,
35 } console_state;
36 
37 typedef enum {
38 	SCREEN_ERASE_WHOLE,
39 	SCREEN_ERASE_UP,
40 	SCREEN_ERASE_DOWN
41 } erase_screen_mode;
42 
43 typedef enum {
44 	LINE_ERASE_WHOLE,
45 	LINE_ERASE_LEFT,
46 	LINE_ERASE_RIGHT
47 } erase_line_mode;
48 
49 #define MAX_ARGS 8
50 
51 static struct console_desc {
52 	mutex	lock;
53 
54 	int32	lines;
55 	int32	columns;
56 
57 	uint8	attr;
58 	uint8	saved_attr;
59 	bool	bright_attr;
60 	bool	reverse_attr;
61 
62 	int32	x;						/* current x coordinate */
63 	int32	y;						/* current y coordinate */
64 	int32	saved_x;				/* used to save x and y */
65 	int32	saved_y;
66 
67 	int32	scroll_top;	/* top of the scroll region */
68 	int32	scroll_bottom;	/* bottom of the scroll region */
69 
70 	/* state machine */
71 	console_state state;
72 	int32	arg_count;
73 	int32	args[MAX_ARGS];
74 
75 	char	module_name[B_FILE_NAME_LENGTH];
76 	console_module_info *module;
77 } sConsole;
78 
79 int32 api_version = B_CUR_DRIVER_API_VERSION;
80 
81 static int32 sOpenMask;
82 
83 
84 static inline void
85 update_cursor(struct console_desc *console, int x, int y)
86 {
87 	console->module->move_cursor(x, y);
88 }
89 
90 
91 static void
92 gotoxy(struct console_desc *console, int newX, int newY)
93 {
94 	if (newX >= console->columns)
95 		newX = console->columns - 1;
96 	if (newX < 0)
97 		newX = 0;
98 	if (newY >= console->lines)
99 		newY = console->lines - 1;
100 	if (newY < 0)
101 		newY = 0;
102 
103 	console->x = newX;
104 	console->y = newY;
105 }
106 
107 
108 static void
109 reset_console(struct console_desc *console)
110 {
111 	console->attr = 0x0f;
112 	console->scroll_top = 0;
113 	console->scroll_bottom = console->lines - 1;
114 	console->bright_attr = true;
115 	console->reverse_attr = false;
116 }
117 
118 
119 /*!	Scroll from the cursor line up to the top of the scroll region up one line.
120 */
121 static void
122 scrup(struct console_desc *console)
123 {
124 	// see if cursor is outside of scroll region
125 	if (console->y < console->scroll_top || console->y > console->scroll_bottom)
126 		return;
127 
128 	if (console->y - console->scroll_top > 1) {
129 		// move the screen up one
130 		console->module->blit(0, console->scroll_top + 1, console->columns,
131 			console->y - console->scroll_top, 0, console->scroll_top);
132 	}
133 
134 	// clear the bottom line
135 	console->module->fill_glyph(0, console->y, console->columns, 1, ' ',
136 		console->attr);
137 }
138 
139 
140 /*!	Scroll from the cursor line down to the bottom of the scroll region down
141 	one line.
142 */
143 static void
144 scrdown(struct console_desc *console)
145 {
146 	// see if cursor is outside of scroll region
147 	if (console->y < console->scroll_top || console->y > console->scroll_bottom)
148 		return;
149 
150 	if (console->scroll_bottom - console->y > 1) {
151 		// move the screen down one
152 		console->module->blit(0, console->y, console->columns,
153 			console->scroll_bottom - console->y, 0, console->y + 1);
154 	}
155 
156 	// clear the top line
157 	console->module->fill_glyph(0, console->y, console->columns, 1, ' ',
158 		console->attr);
159 }
160 
161 
162 static void
163 lf(struct console_desc *console)
164 {
165 	//dprintf("lf: y %d x %d scroll_top %d scoll_bottom %d\n", console->y, console->x, console->scroll_top, console->scroll_bottom);
166 
167 	if (console->y == console->scroll_bottom ) {
168  		// we hit the bottom of our scroll region
169  		scrup(console);
170 	} else if(console->y < console->scroll_bottom) {
171 		console->y++;
172 	}
173 }
174 
175 
176 static void
177 rlf(struct console_desc *console)
178 {
179 	if (console->y == console->scroll_top) {
180  		// we hit the top of our scroll region
181  		scrdown(console);
182 	} else if (console->y > console->scroll_top) {
183 		console->y--;
184 	}
185 }
186 
187 
188 static void
189 cr(struct console_desc *console)
190 {
191 	console->x = 0;
192 }
193 
194 
195 static void
196 del(struct console_desc *console)
197 {
198 	if (console->x > 0) {
199 		console->x--;
200 	} else if (console->y > 0) {
201         console->y--;
202         console->x = console->columns - 1;
203     } else {
204         //This doesn't work...
205         //scrdown(console);
206         //console->y--;
207         //console->x = console->columns - 1;
208         return;
209     }
210 	console->module->put_glyph(console->x, console->y, ' ', console->attr);
211 }
212 
213 
214 static void
215 erase_line(struct console_desc *console, erase_line_mode mode)
216 {
217 	switch (mode) {
218 		case LINE_ERASE_WHOLE:
219 			console->module->fill_glyph(0, console->y, console->columns, 1, ' ',
220 				console->attr);
221 			break;
222 		case LINE_ERASE_LEFT:
223 			console->module->fill_glyph(0, console->y, console->x + 1, 1, ' ',
224 				console->attr);
225 			break;
226 		case LINE_ERASE_RIGHT:
227 			console->module->fill_glyph(console->x, console->y,
228 				console->columns - console->x, 1, ' ', console->attr);
229 			break;
230 		default:
231 			return;
232 	}
233 }
234 
235 
236 static void
237 erase_screen(struct console_desc *console, erase_screen_mode mode)
238 {
239 	switch (mode) {
240 		case SCREEN_ERASE_WHOLE:
241 			console->module->clear(console->attr);
242 			break;
243 		case SCREEN_ERASE_UP:
244 			console->module->fill_glyph(0, 0, console->columns, console->y + 1,
245 				' ', console->attr);
246 			break;
247 		case SCREEN_ERASE_DOWN:
248 			console->module->fill_glyph(console->y, 0, console->columns,
249 				console->lines - console->y, ' ', console->attr);
250 			break;
251 		default:
252 			return;
253 	}
254 }
255 
256 
257 static void
258 save_cur(struct console_desc *console, bool saveAttrs)
259 {
260 	console->saved_x = console->x;
261 	console->saved_y = console->y;
262 
263 	if (saveAttrs)
264 		console->saved_attr = console->attr;
265 }
266 
267 
268 static void
269 restore_cur(struct console_desc *console, bool restoreAttrs)
270 {
271 	console->x = console->saved_x;
272 	console->y = console->saved_y;
273 
274 	if (restoreAttrs)
275 		console->attr = console->saved_attr;
276 }
277 
278 
279 static char
280 console_putch(struct console_desc *console, const char c)
281 {
282 	if (++console->x >= console->columns) {
283 		cr(console);
284 		lf(console);
285 	}
286 	console->module->put_glyph(console->x - 1, console->y, c, console->attr);
287 	return c;
288 }
289 
290 
291 static void
292 tab(struct console_desc *console)
293 {
294 	console->x = (console->x + TAB_SIZE) & ~TAB_MASK;
295 	if (console->x >= console->columns) {
296 		console->x -= console->columns;
297 		lf(console);
298 	}
299 }
300 
301 
302 static void
303 set_scroll_region(struct console_desc *console, int top, int bottom)
304 {
305 	if (top < 0)
306 		top = 0;
307 	if (bottom >= console->lines)
308 		bottom = console->lines - 1;
309 	if (top > bottom)
310 		return;
311 
312 	console->scroll_top = top;
313 	console->scroll_bottom = bottom;
314 }
315 
316 
317 static void
318 set_vt100_attributes(struct console_desc *console, int32 *args, int32 argCount)
319 {
320 	if (argCount == 0) {
321 		// that's the default (attributes off)
322 		argCount++;
323 		args[0] = 0;
324 	}
325 
326 	for (int32 i = 0; i < argCount; i++) {
327 		//dprintf("set_vt100_attributes: %ld\n", args[i]);
328 		switch (args[i]) {
329 			case 0: // reset
330 				console->attr = 0x0f;
331 				console->bright_attr = true;
332 				console->reverse_attr = false;
333 				break;
334 			case 1: // bright
335 				console->bright_attr = true;
336 				console->attr |= 0x08; // set the bright bit
337 				break;
338 			case 2: // dim
339 				console->bright_attr = false;
340 				console->attr &= ~0x08; // unset the bright bit
341 				break;
342 			case 4: // underscore we can't do
343 				break;
344 			case 5: // blink
345 				console->attr |= 0x80; // set the blink bit
346 				break;
347 			case 7: // reverse
348 				console->reverse_attr = true;
349 				console->attr = ((console->attr & BMASK) >> 4)
350 					| ((console->attr & FMASK) << 4);
351 				if (console->bright_attr)
352 					console->attr |= 0x08;
353 				break;
354 			case 8: // hidden?
355 				break;
356 
357 			/* foreground colors */
358 			case 30: console->attr = (console->attr & ~FMASK) | 0 | (console->bright_attr ? 0x08 : 0); break; // black
359 			case 31: console->attr = (console->attr & ~FMASK) | 4 | (console->bright_attr ? 0x08 : 0); break; // red
360 			case 32: console->attr = (console->attr & ~FMASK) | 2 | (console->bright_attr ? 0x08 : 0); break; // green
361 			case 33: console->attr = (console->attr & ~FMASK) | 6 | (console->bright_attr ? 0x08 : 0); break; // yellow
362 			case 34: console->attr = (console->attr & ~FMASK) | 1 | (console->bright_attr ? 0x08 : 0); break; // blue
363 			case 35: console->attr = (console->attr & ~FMASK) | 5 | (console->bright_attr ? 0x08 : 0); break; // magenta
364 			case 36: console->attr = (console->attr & ~FMASK) | 3 | (console->bright_attr ? 0x08 : 0); break; // cyan
365 			case 37: console->attr = (console->attr & ~FMASK) | 7 | (console->bright_attr ? 0x08 : 0); break; // white
366 
367 			/* background colors */
368 			case 40: console->attr = (console->attr & ~BMASK) | (0 << 4); break; // black
369 			case 41: console->attr = (console->attr & ~BMASK) | (4 << 4); break; // red
370 			case 42: console->attr = (console->attr & ~BMASK) | (2 << 4); break; // green
371 			case 43: console->attr = (console->attr & ~BMASK) | (6 << 4); break; // yellow
372 			case 44: console->attr = (console->attr & ~BMASK) | (1 << 4); break; // blue
373 			case 45: console->attr = (console->attr & ~BMASK) | (5 << 4); break; // magenta
374 			case 46: console->attr = (console->attr & ~BMASK) | (3 << 4); break; // cyan
375 			case 47: console->attr = (console->attr & ~BMASK) | (7 << 4); break; // white
376 		}
377 	}
378 }
379 
380 
381 static bool
382 process_vt100_command(struct console_desc *console, const char c,
383 	bool seenBracket, int32 *args, int32 argCount)
384 {
385 	bool ret = true;
386 
387 	//dprintf("process_vt100_command: c '%c', argCount %ld, arg[0] %ld, arg[1] %ld, seenBracket %d\n",
388 	//	c, argCount, args[0], args[1], seenBracket);
389 
390 	if (seenBracket) {
391 		switch(c) {
392 			case 'H': // set cursor position
393 			case 'f':
394 			{
395 				int32 row = argCount > 0 ? args[0] : 1;
396 				int32 col = argCount > 1 ? args[1] : 1;
397 				if (row > 0)
398 					row--;
399 				if (col > 0)
400 					col--;
401 				gotoxy(console, col, row);
402 				break;
403 			}
404 			case 'A':	// move up
405 			{
406 				int32 deltaY = argCount > 0 ? -args[0] : -1;
407 				if (deltaY == 0)
408 					deltaY = -1;
409 				gotoxy(console, console->x, console->y + deltaY);
410 				break;
411 			}
412 			case 'e':
413 			case 'B':	// move down
414 			{
415 				int32 deltaY = argCount > 0 ? args[0] : 1;
416 				if (deltaY == 0)
417 					deltaY = 1;
418 				gotoxy(console, console->x, console->y + deltaY);
419 				break;
420 			}
421 			case 'D':	// move left
422 			{
423 				int32 deltaX = argCount > 0 ? -args[0] : -1;
424 				if (deltaX == 0)
425 					deltaX = -1;
426 				gotoxy(console, console->x + deltaX, console->y);
427 				break;
428 			}
429 			case 'a':
430 			case 'C':	// move right
431 			{
432 				int32 deltaX = argCount > 0 ? args[0] : 1;
433 				if (deltaX == 0)
434 					deltaX = 1;
435 				gotoxy(console, console->x + deltaX, console->y);
436 				break;
437 			}
438 			case '`':
439 			case 'G':	// set X position
440 			{
441 				int32 newX = argCount > 0 ? args[0] : 1;
442 				if (newX > 0)
443 					newX--;
444 				gotoxy(console, newX, console->y);
445 				break;
446 			}
447 			case 'd':	// set y position
448 			{
449 				int32 newY = argCount > 0 ? args[0] : 1;
450 				if (newY > 0)
451 					newY--;
452 				gotoxy(console, console->x, newY);
453 				break;
454 			}
455 			case 's':	// save current cursor
456 				save_cur(console, false);
457 				break;
458 			case 'u':	// restore cursor
459 				restore_cur(console, false);
460 				break;
461 			case 'r':	// set scroll region
462 			{
463 				int32 low = argCount > 0 ? args[0] : 1;
464 				int32 high = argCount > 1 ? args[1] : console->lines;
465 				if (low <= high)
466 					set_scroll_region(console, low - 1, high - 1);
467 				break;
468 			}
469 			case 'L':	// scroll virtual down at cursor
470 			{
471 				int32 lines = argCount > 0 ? args[0] : 1;
472 				while (lines > 0) {
473 					scrdown(console);
474 					lines--;
475 				}
476 				break;
477 			}
478 			case 'M':	// scroll virtual up at cursor
479 			{
480 				int32 lines = argCount > 0 ? args[0] : 1;
481 				while (lines > 0) {
482 					scrup(console);
483 					lines--;
484 				}
485 				break;
486 			}
487 			case 'K':
488 				if (argCount == 0 || args[0] == 0) {
489 					// erase to end of line
490 					erase_line(console, LINE_ERASE_RIGHT);
491 				} else if (argCount > 0) {
492 					if (args[0] == 1)
493 						erase_line(console, LINE_ERASE_LEFT);
494 					else if (args[0] == 2)
495 						erase_line(console, LINE_ERASE_WHOLE);
496 				}
497 				break;
498 			case 'J':
499 				if (argCount == 0 || args[0] == 0) {
500 					// erase to end of screen
501 					erase_screen(console, SCREEN_ERASE_DOWN);
502 				} else {
503 					if (args[0] == 1)
504 						erase_screen(console, SCREEN_ERASE_UP);
505 					else if (args[0] == 2)
506 						erase_screen(console, SCREEN_ERASE_WHOLE);
507 				}
508 				break;
509 			case 'm':
510 				if (argCount >= 0)
511 					set_vt100_attributes(console, args, argCount);
512 				break;
513 			default:
514 				ret = false;
515 		}
516 	} else {
517 		switch (c) {
518 			case 'c':
519 				reset_console(console);
520 				break;
521 			case 'D':
522 				rlf(console);
523 				break;
524 			case 'M':
525 				lf(console);
526 				break;
527 			case '7':
528 				save_cur(console, true);
529 				break;
530 			case '8':
531 				restore_cur(console, true);
532 				break;
533 			default:
534 				ret = false;
535 		}
536 	}
537 
538 	return ret;
539 }
540 
541 
542 static ssize_t
543 _console_write(struct console_desc *console, const void *buffer, size_t length)
544 {
545 	const char *c;
546 	size_t pos = 0;
547 
548 	while (pos < length) {
549 		c = &((const char *)buffer)[pos++];
550 
551 		switch (console->state) {
552 			case CONSOLE_STATE_NORMAL:
553 				// just output the stuff
554 				switch (*c) {
555 					case '\n':
556 						lf(console);
557 						break;
558 					case '\r':
559 						cr(console);
560 						break;
561 					case 0x8: // backspace
562 						del(console);
563 						break;
564 					case '\t':
565 						tab(console);
566 						break;
567 					case '\a':
568 						// beep
569 						dprintf("<BEEP>\n");
570 						break;
571 					case '\0':
572 						break;
573 					case 0x1b:
574 						// escape character
575 						console->arg_count = 0;
576 						console->state = CONSOLE_STATE_GOT_ESCAPE;
577 						break;
578 					default:
579 						console_putch(console, *c);
580 				}
581 				break;
582 			case CONSOLE_STATE_GOT_ESCAPE:
583 				// Look for either commands with no argument, or the '['
584 				// character
585 				switch (*c) {
586 					case '[':
587 						console->state = CONSOLE_STATE_SEEN_BRACKET;
588 						break;
589 					default:
590 						console->args[0] = 0;
591 						process_vt100_command(console, *c, false, console->args,
592 							0);
593 						console->state = CONSOLE_STATE_NORMAL;
594 						break;
595 				}
596 				break;
597 			case CONSOLE_STATE_SEEN_BRACKET:
598 				switch (*c) {
599 					case '0'...'9':
600 						console->arg_count = 0;
601 						console->args[console->arg_count] = *c - '0';
602 						console->state = CONSOLE_STATE_PARSING_ARG;
603 						break;
604 					case '?':
605 						// Private DEC mode parameter follows - we ignore those
606 						// anyway
607 						// TODO: check if it was really used in combination with
608 						// a mode command
609 						break;
610 					default:
611 						process_vt100_command(console, *c, true, console->args,
612 							0);
613 						console->state = CONSOLE_STATE_NORMAL;
614 						break;
615 				}
616 				break;
617 			case CONSOLE_STATE_NEW_ARG:
618 				switch (*c) {
619 					case '0'...'9':
620 						console->arg_count++;
621 						if (console->arg_count == MAX_ARGS) {
622 							console->state = CONSOLE_STATE_NORMAL;
623 							break;
624 						}
625 						console->args[console->arg_count] = *c - '0';
626 						console->state = CONSOLE_STATE_PARSING_ARG;
627 						break;
628 					default:
629 						process_vt100_command(console, *c, true, console->args,
630 							console->arg_count + 1);
631 						console->state = CONSOLE_STATE_NORMAL;
632 						break;
633 				}
634 				break;
635 			case CONSOLE_STATE_PARSING_ARG:
636 				// parse args
637 				switch (*c) {
638 					case '0'...'9':
639 						console->args[console->arg_count] *= 10;
640 						console->args[console->arg_count] += *c - '0';
641 						break;
642 					case ';':
643 						console->state = CONSOLE_STATE_NEW_ARG;
644 						break;
645 					default:
646 						process_vt100_command(console, *c, true, console->args,
647 							console->arg_count + 1);
648 						console->state = CONSOLE_STATE_NORMAL;
649 						break;
650 				}
651 		}
652 	}
653 
654 	return pos;
655 }
656 
657 
658 //	#pragma mark -
659 
660 
661 static status_t
662 console_open(const char *name, uint32 flags, void **cookie)
663 {
664 	if (atomic_or(&sOpenMask, 1) == 1)
665 		return B_BUSY;
666 
667 	*cookie = &sConsole;
668 
669 	status_t status = get_module(sConsole.module_name, (module_info **)&sConsole.module);
670 	if (status == B_OK)
671 		sConsole.module->clear(0x0f);
672 
673 	return status;
674 }
675 
676 
677 static status_t
678 console_freecookie(void *cookie)
679 {
680 	if (sConsole.module != NULL) {
681 		put_module(sConsole.module_name);
682 		sConsole.module = NULL;
683 	}
684 
685 	atomic_and(&sOpenMask, ~1);
686 
687 	return B_OK;
688 }
689 
690 
691 static status_t
692 console_close(void *cookie)
693 {
694 //	dprintf("console_close: entry\n");
695 
696 	return 0;
697 }
698 
699 
700 static status_t
701 console_read(void *cookie, off_t pos, void *buffer, size_t *_length)
702 {
703 	return B_NOT_ALLOWED;
704 }
705 
706 
707 static status_t
708 console_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
709 {
710 	struct console_desc *console = (struct console_desc *)cookie;
711 	ssize_t written = 0;
712 	status_t status = B_OK;
713 
714 #if 0
715 {
716 	const char *input = (const char *)buffer;
717 	dprintf("console_write (%lu bytes): \"", *_length);
718 	for (uint32 i = 0; i < *_length; i++) {
719 		if (input[i] < ' ')
720 			dprintf("(%d:0x%x)", input[i], input[i]);
721 		else
722 			dprintf("%c", input[i]);
723 	}
724 	dprintf("\"\n");
725 }
726 #endif
727 
728 	mutex_lock(&console->lock);
729 
730 	update_cursor(console, -1, -1); // hide it
731 
732 	size_t bytesLeft = *_length;
733 	const char *str = (const char*)buffer;
734 	while (bytesLeft > 0) {
735 		char localBuffer[512];
736 		size_t chunkSize = min_c(sizeof(localBuffer), bytesLeft);
737 		if (user_memcpy(localBuffer, str, chunkSize) < B_OK) {
738 			status = B_BAD_ADDRESS;
739 			break;
740 		}
741 		written += _console_write(console, localBuffer, chunkSize);
742 		str += chunkSize;
743 		bytesLeft -= chunkSize;
744 	}
745 	update_cursor(console, console->x, console->y);
746 
747 	mutex_unlock(&console->lock);
748 
749 	if (status == B_OK)
750 		*_length = written;
751 	return status;
752 }
753 
754 
755 static status_t
756 console_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
757 {
758 	struct console_desc *console = (struct console_desc *)cookie;
759 
760 	if (op == TIOCGWINSZ) {
761 		struct winsize size;
762 		size.ws_xpixel = size.ws_col = console->columns;
763 		size.ws_ypixel = size.ws_row = console->lines;
764 
765 		return user_memcpy(buffer, &size, sizeof(struct winsize));
766 	}
767 
768 	return B_BAD_VALUE;
769 }
770 
771 
772 //	#pragma mark -
773 
774 
775 status_t
776 init_hardware(void)
777 {
778 	// iterate through the list of console modules until we find one that accepts the job
779 	void *cookie = open_module_list("console");
780 	if (cookie == NULL)
781 		return B_ERROR;
782 
783 	bool found = false;
784 
785 	char buffer[B_FILE_NAME_LENGTH];
786 	size_t bufferSize = sizeof(buffer);
787 
788 	while (read_next_module_name(cookie, buffer, &bufferSize) == B_OK) {
789 		dprintf("con_init: trying module %s\n", buffer);
790 		if (get_module(buffer, (module_info **)&sConsole.module) == B_OK) {
791 			strlcpy(sConsole.module_name, buffer, sizeof(sConsole.module_name));
792 			put_module(buffer);
793 			found = true;
794 			break;
795 		}
796 
797 		bufferSize = sizeof(buffer);
798 	}
799 
800 	if (found) {
801 		// set up the console structure
802 		mutex_init(&sConsole.lock, "console lock");
803 		sConsole.module->get_size(&sConsole.columns, &sConsole.lines);
804 
805 		reset_console(&sConsole);
806 		gotoxy(&sConsole, 0, 0);
807 		save_cur(&sConsole, true);
808 	}
809 
810 	close_module_list(cookie);
811 	return found ? B_OK : B_ERROR;
812 }
813 
814 
815 const char **
816 publish_devices(void)
817 {
818 	static const char *devices[] = {
819 		DEVICE_NAME,
820 		NULL
821 	};
822 
823 	return devices;
824 }
825 
826 
827 device_hooks *
828 find_device(const char *name)
829 {
830 	static device_hooks hooks = {
831 		&console_open,
832 		&console_close,
833 		&console_freecookie,
834 		&console_ioctl,
835 		&console_read,
836 		&console_write,
837 		/* Leave select/deselect/readv/writev undefined. The kernel will
838 		 * use its own default implementation. The basic hooks above this
839 		 * line MUST be defined, however. */
840 		NULL,
841 		NULL,
842 		NULL,
843 		NULL
844 	};
845 
846 	if (!strcmp(name, DEVICE_NAME))
847 		return &hooks;
848 
849 	return NULL;
850 }
851 
852 
853 status_t
854 init_driver(void)
855 {
856 	return B_OK;
857 }
858 
859 
860 void
861 uninit_driver(void)
862 {
863 }
864 
865