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