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
update_cursor(struct console_desc * console,int x,int y)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
gotoxy(struct console_desc * console,int newX,int newY)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
reset_console(struct console_desc * console)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
scrup(struct console_desc * console)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
scrdown(struct console_desc * console)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
lf(struct console_desc * console)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
rlf(struct console_desc * console)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
cr(struct console_desc * console)189 cr(struct console_desc *console)
190 {
191 console->x = 0;
192 }
193
194
195 static void
del(struct console_desc * console)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
erase_line(struct console_desc * console,erase_line_mode mode)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
erase_screen(struct console_desc * console,erase_screen_mode mode)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
save_cur(struct console_desc * console,bool saveAttrs)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
restore_cur(struct console_desc * console,bool restoreAttrs)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
console_putch(struct console_desc * console,const char c)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
tab(struct console_desc * console)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
set_scroll_region(struct console_desc * console,int top,int bottom)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
set_vt100_attributes(struct console_desc * console,int32 * args,int32 argCount)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
process_vt100_command(struct console_desc * console,const char c,bool seenBracket,int32 * args,int32 argCount)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
_console_write(struct console_desc * console,const void * buffer,size_t length)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
console_open(const char * name,uint32 flags,void ** cookie)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
console_freecookie(void * cookie)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
console_close(void * cookie)692 console_close(void *cookie)
693 {
694 // dprintf("console_close: entry\n");
695
696 return 0;
697 }
698
699
700 static status_t
console_read(void * cookie,off_t pos,void * buffer,size_t * _length)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
console_write(void * cookie,off_t pos,const void * buffer,size_t * _length)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
console_ioctl(void * cookie,uint32 op,void * buffer,size_t length)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
init_hardware(void)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 **
publish_devices(void)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 *
find_device(const char * name)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
init_driver(void)854 init_driver(void)
855 {
856 return B_OK;
857 }
858
859
860 void
uninit_driver(void)861 uninit_driver(void)
862 {
863 }
864
865