/* * @OSF_FREE_FREE_COPYRIGHT@ * */ /* * HISTORY * $Log: console.c,v $ * Revision 1.1.2.4 1997/01/21 19:07:32 barbou * Moved osfmach3_video_* definitions here from drivers/char/vga.c. * [1997/01/21 18:42:46 barbou] * * Revision 1.1.2.3 1996/09/25 16:44:44 barbou * In cngetc, allow 0xff character to be read on console: don't expand * it to 0xffffffff (i.e. error) when returning it as an integer. * [96/09/25 barbou] * * Revision 1.1.2.2 1996/09/11 14:19:36 barbou * Added osfmach3_use_mach_console. * [96/09/11 barbou] * * Revision 1.1.2.1 1996/09/09 17:15:55 barbou * In osfmach3_launch_console_read_thread, make sure that we start only * one thread. Don't start one if we use the raw keyboard device. * [96/09/09 barbou] * * Added osfmach3_launch_console_read_thread. * [96/09/05 barbou] * * Added basic console ouput routines. * [96/08/26 barbou] * * Created. * [1996/08/26 14:03:05 barbou] * * $EndLog$ */ #include <cthreads.h> #include <osfmach3/parent_server.h> #include <osfmach3/console.h> #include <osfmach3/uniproc.h> #include <osfmach3/server_thread.h> #include <osfmach3/device_utils.h> #include <osfmach3/char_dev.h> #include <osfmach3/mach3_debug.h> #include <linux/string.h> #include <linux/kernel.h> #include <linux/termios.h> #include <linux/tty_driver.h> #include <linux/major.h> #include <linux/tty.h> #include <linux/errno.h> extern void register_console(void (*proc)(const char *)); extern struct tty_driver console_driver; static int osfmach3_console_refcount; static struct tty_struct *osfmach3_console_table[MAX_NR_CONSOLES]; static struct termios *osfmach3_console_termios[MAX_NR_CONSOLES]; static struct termios *osfmach3_console_termios_locked[MAX_NR_CONSOLES]; int osfmach3_con_refs[MAX_NR_CONSOLES] = {0, }; memory_object_t osfmach3_video_memory_object; vm_address_t osfmach3_video_physaddr; vm_address_t osfmach3_video_map_base; vm_size_t osfmach3_video_map_size = 0; unsigned long osfmach3_video_offset = 0; mach_port_t osfmach3_video_port = MACH_PORT_NULL; /* * Use Mach console instead of a video adapter + raw keyboard ? * This is useful when using the remote Mach console on a serial line, * to have the Linux console on the serial line too. */ int osfmach3_use_mach_console = 0; int osfmach3_con_open(struct tty_struct *tty, struct file *filp) { int result; result = osfmach3_tty_open(tty); if (result) return result; #ifdef PPC /* * XXX * This is mostly a hack to get the console size. */ { struct vc_info { unsigned long v_height; unsigned long v_width; unsigned long v_depth; unsigned long v_rowbytes; unsigned long v_baseaddr; unsigned long v_type; char v_name[32]; unsigned long v_reserved[9]; } vi; #define VC_GETINFO (('v'<<16)|1) kern_return_t kr; mach_msg_type_number_t count; count = sizeof vi / sizeof (int); kr = device_get_status(osfmach3_console_port, VC_GETINFO, (dev_status_t) &vi, &count); if (kr == D_SUCCESS) { struct tty_struct *tty; tty = console_driver.table[0]; /* the following assumes a 8x16 character size */ tty->winsize.ws_row = vi.v_height / 16; tty->winsize.ws_col = vi.v_width / 8; } } #endif /* PPC */ return 0; } int osfmach3_con_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { int c, n = 0; while (!tty->stopped && count) { c = from_user ? get_fs_byte(buf) : *buf; buf++; n++; count--; cnputc(c); } cnflush(); return n; } int osfmach3_con_write_room(struct tty_struct *tty) { if (tty->stopped) return 0; return 4096; /* No limit, really: we're not buffering */ } int osfmach3_con_chars_in_buffer(struct tty_struct *tty) { return 0; /* we're not buffering */ } void osfmach3_con_stop(struct tty_struct *tty) { } void osfmach3_con_start(struct tty_struct *tty) { #if 0 if (! parent_server) { osfmach3_tty_start(tty); } #endif } void osfmach3_con_throttle(struct tty_struct *tty) { } void osfmach3_con_unthrottle(struct tty_struct *tty) { } int osfmach3_vt_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } unsigned long osfmach3_con_init(unsigned long kmem_start) { int ret; memset(&console_driver, 0, sizeof (struct tty_driver)); console_driver.magic = TTY_DRIVER_MAGIC; console_driver.name = "tty"; console_driver.name_base = 1; console_driver.major = TTY_MAJOR; console_driver.minor_start = 1; console_driver.num = MAX_NR_CONSOLES; console_driver.type = TTY_DRIVER_TYPE_CONSOLE; console_driver.init_termios = tty_std_termios; console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; console_driver.refcount = &osfmach3_console_refcount; console_driver.table = osfmach3_console_table; console_driver.termios = osfmach3_console_termios; console_driver.termios_locked = osfmach3_console_termios_locked; console_driver.open = osfmach3_con_open; console_driver.write = osfmach3_con_write; console_driver.write_room = osfmach3_con_write_room; console_driver.chars_in_buffer = osfmach3_con_chars_in_buffer; console_driver.ioctl = osfmach3_vt_ioctl; console_driver.stop = osfmach3_con_stop; console_driver.start = osfmach3_con_start; console_driver.throttle = osfmach3_con_throttle; console_driver.unthrottle = osfmach3_con_unthrottle; if (tty_register_driver(&console_driver)) panic("Couldn't register console driver\n"); ret = osfmach3_register_chrdev(console_driver.major, console_driver.name, console_driver.minor_start + console_driver.num, osfmach3_tty_dev_to_name, osfmach3_con_refs); if (ret) { printk("con_init: osfmach3_register_chrdev failed %d\n", ret); panic("con_init: can't register Mach device"); } if (parent_server) { printk("Console: parent process's terminal\n"); } else { printk("Console: Mach console\n"); } #if 0 /* already done in osfmach3_console_init() */ register_console(console_print); #endif return kmem_start; } void * console_read_thread( void *tty_handle) { struct server_thread_priv_data priv_data; struct tty_struct *tty; int ret; unsigned char c; struct tty_struct **ttytab; cthread_set_name(cthread_self(), "console read"); server_thread_set_priv_data(cthread_self(), &priv_data); tty = (struct tty_struct *) tty_handle; ttytab = console_driver.table; uniproc_enter(); for (;;) { ret = cngetc(); if (ret == -1) panic("console_read_thread: cngetc returned -1!\n"); c = (unsigned char) ret; if (tty_handle == NULL) { /* * Virtual console: find the active one. */ tty = ttytab[fg_console]; } tty->ldisc.receive_buf(tty, &c, 0, 1); } /*NOTREACHED*/ } void osfmach3_launch_console_read_thread( void *tty_handle) { static boolean_t launched = FALSE; if (launched || osfmach3_keyboard_port != MACH_PORT_NULL) { /* * There is already a console_read_thread running, * or we don't need one since we use the raw keyboard driver * directly. */ return; } launched = TRUE; server_thread_start(console_read_thread, tty_handle); } /* * Console output. */ mach_port_t osfmach3_console_port; /* * device_write_inband() can't deal with more than 128 characters at * a time. It will return MIG_ARRAY_TOO_LARGE otherwise... * Check the file {mk_release_directory}/include/device/device.defs for * the MiG stub, and device_types.defs for the argument type and its length * restriction. */ #define CONS_BUF_SIZE 128 struct { char chars[CONS_BUF_SIZE]; int pos; int initialized; } cons_buf; int cnputc( int c) { cons_buf.chars[cons_buf.pos++] = c; if (cons_buf.pos >= CONS_BUF_SIZE || c == '\n' || c == '\r') { cnflush(); } return 0; } void cnflush(void) { if (parent_server) parent_server_write(1, cons_buf.chars, cons_buf.pos); else { unsigned int cw; int start_pos; kern_return_t kr; kr = D_SUCCESS; start_pos = 0; while (start_pos < cons_buf.pos) { kr = device_write_inband(osfmach3_console_port, 0, 0, &cons_buf.chars[start_pos], cons_buf.pos - start_pos, &cw); start_pos += cw; } if (kr != D_SUCCESS) { char *text = "panic: cnflush.\n\r"; (void) device_write_inband(osfmach3_console_port, 0, 0, text, strlen(text), &cw); kr /= 0; /* suicide, since panic may fail */ } start_pos += cw; } cons_buf.pos = 0; } void osfmach3_console_print(const char *b) { char c; while ((c = *(b++)) != 0) { cnputc(c); if (c == '\n') { cnputc('\r'); cnflush(); } } cnflush(); } int osfmach3_console_initialized = 0; void osfmach3_console_init(void) { kern_return_t kr; if (osfmach3_console_initialized != 0) return; osfmach3_console_initialized = 1; if (parent_server) { parent_server_grab_console(); } else { /* * Open the console */ kr = device_open(device_server_port, MACH_PORT_NULL, D_READ|D_WRITE, server_security_token, "console", &osfmach3_console_port); if (kr != KERN_SUCCESS) panic("console_init"); } register_console(osfmach3_console_print); } int cngetc(void) { char c; int n; kern_return_t kr; if (parent_server) { server_thread_blocking(FALSE); n = parent_server_read(0, &c, sizeof c); server_thread_unblocking(FALSE); if (n <= 0) { if (n < 0) printk("cngetc: read error %d\n", parent_server_errno); else printk("cngetc: EOF\n"); return -1; } return (int) c; } n = sizeof c; server_thread_blocking(FALSE); kr = device_read_inband(osfmach3_console_port, 0, 0, sizeof c, &c, &n); server_thread_unblocking(FALSE); if (kr != D_SUCCESS) { MACH3_DEBUG(0, kr, ("cngetc: read")); return -1; } if (n <= 0) { printk("cngetc: read returned %d bytes", n); return -1; } return ((int) c) & 0xff; } /* Get string with minimal line editing. Based on kernel/i386/rdb/gets.c. Note that there is no checking for overflow of the buffer. Also, on the second server our attempts at echoing and editing are probably for naught since the second console is most likely still in cooked mode when we are called. But the only visible effect in that case is that the entered line gets printed in its entirety once you hit RETURN, since we get and echo all the entered characters at once. */ char *gets( char buf[GETS_MAX]) { int c, i; i = 0; for (;;) { c = cngetc(); switch (c) { case '\n': case '\r': cnputc('\n'); cnputc('\r'); cnflush(); buf[i] = '\0'; return(buf); case '\b': case '#': if (i > 0) { cnputc('\b'); cnputc(' '); cnputc('\b'); cnflush(); i--; } continue; case '@': case 'u' & 037: i = 0; cnputc('\n'); cnputc('\r'); cnflush(); continue; case -1: case '\04': /* ^D - return null pointer */ cnputc('\n'); cnputc('\r'); cnflush(); return((char *) 0); default: if (i >= GETS_MAX - 1) cnputc('\a'); else { cnputc(c); buf[i++] = c; } cnflush(); } } }