📄 vt.c
字号:
/* * linux/arch/arm/drivers/char/vt.c * * VT routines * * Changelog: * 05-Sep-1996 RMK Fixed race condition between VT switch & initialisation * 08-Sep-1996 RMK Adapted Brad Pepers (ramparts@agt.net) console buffering code * (vt_put_char & vt_flush_chars). * 02-Sep-1997 RMK Added in VT switch disable */#include <linux/config.h>#include <linux/sched.h>#include <linux/tty.h>#include <linux/kd.h>#include <linux/errno.h>#include <linux/malloc.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/major.h>#include <asm/segment.h>#include <asm/hardware.h>#include "kbd_kern.h"#include "vt_kern.h"#define CON_XMIT_SIZE 2048#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif/* * VCD functions */extern void vcd_blankscreen (int nopowersave);extern void vcd_disallocate (struct vt *);extern int vcd_init (struct vt *, int kmallocok, unsigned long *kmem);extern int vcd_ioctl (struct vt *, int cmd, unsigned long arg);extern unsigned long vcd_pre_init (unsigned long kmem, struct vt *);extern int vcd_resize (int rows, int columns);extern void vcd_restorestate (const struct vt *);extern void vcd_savestate (const struct vt *, int blanked);extern void vcd_unblankscreen (void);extern int vcd_write (const struct vt *, int from_user, const unsigned char *buf, int count);extern void vcd_setup_graphics (const struct vt *);/* * Console functions */extern void con_reset_palette (const struct vt *vt);extern void con_set_palette (const struct vt *vt);extern int con_init (void);static int vt_refcount;int do_poke_blanked_console;static struct tty_driver vt_driver;static struct tty_struct *vt_table[MAX_NR_CONSOLES];static struct termios *vt_termios[MAX_NR_CONSOLES];static struct termios *vt_termios_locked[MAX_NR_CONSOLES];struct vt_data vtdata;struct vt vt_con_data[MAX_NR_CONSOLES];extern void vt_do_blankscreen (int nopowersave);extern void vt_do_unblankscreen (void);/* set this to the sound driver's wave output trigger */void (*mksound_hook)(unsigned int freq, unsigned int vol, unsigned int duration);/* * last_console is the last used console */struct vt *last_console;struct vt *want_console;static char vt_dont_switch = 0;/* * Sometimes we want to wait until a particular VT has been activated. We * do it in a very simple manner. Everybody waits on a single queue and * get woken up at once. Those that are satisfied go on with their business, * while those not ready go back to sleep. Seems overkill to add a wait * to each vt just for this - usually this does nothing! */static struct wait_queue *vt_activate_queue = NULL;/* * Sleeps until a vt is activated, or the task is interrupted. Returns * 0 if activation, -1 if interrupted. */static int vt_waitonactivate (void){ interruptible_sleep_on (&vt_activate_queue); return (current->signal & ~current->blocked) ? -1 : 0;}#define vt_wake_waitactive() wake_up(&vt_activate_queue)void vt_reset (const struct vt *vt){ vt->kbd->kbdmode = VC_XLATE; vt->vtd->vc_mode = KD_TEXT; vt->vtd->vt_mode.mode = VT_AUTO; vt->vtd->vt_mode.waitv = 0; vt->vtd->vt_mode.relsig = 0; vt->vtd->vt_mode.acqsig = 0; vt->vtd->vt_mode.frsig = 0; vt->vtd->vt_pid = -1; vt->vtd->vt_newvt = NULL; con_reset_palette (vt);}static int vt_allocate (struct vt *vt){ if (!vt_allocated(vt)) { void *data, *p; int r; data = kmalloc (sizeof (*vt->vcd) + sizeof (*vt->kbd) + sizeof (*vt->vtd) + CON_XMIT_SIZE, GFP_KERNEL); if (!data) return -ENOMEM; vt->vcd = data; p = ((struct con_struct *)data + 1); vt->kbd = p; p = ((struct kbd_struct *)p + 1); vt->vtd = p; p = ((struct vt_struct *)p + 1); vt->vtd->xmit_buf = p; if ((r = kbd_struct_init (vt, 1)) < 0) { vt->vcd = NULL; vt->kbd = NULL; vt->vtd = NULL; kfree (data); return r; } if ((r = vcd_init (vt, 1, NULL)) < 0) { vt->vcd = NULL; vt->kbd = NULL; vt->vtd = NULL; kfree (data); return r; } vt_reset (vt); vt->vtd->xmitting = vt->vtd->xmit_cnt = vt->vtd->xmit_out = vt->vtd->xmit_in = 0; vt->allocinit = 1; } return 0;}static int vt_disallocate (struct vt *vt){ if (vt_allocated (vt)) { void *data = vt->vcd; vt->allocinit = 0; vcd_disallocate (vt); vt->vcd = NULL; vt->kbd = NULL; vt->vtd = NULL; kfree (data); } return 0;}void vt_updatescreen (const struct vt *newvt){ static int lock = 0; if (newvt == vtdata.fgconsole || lock) return; if (!vt_allocated (newvt)) { printk ("updatescreen: tty %d not allocated ??\n", newvt->num); return; } lock = 1; vcd_savestate (vtdata.fgconsole, vtdata.blanked != NULL); vtdata.fgconsole = (struct vt *)newvt; vcd_restorestate (vtdata.fgconsole); compute_shiftstate (); lock = 0;}/* * Performs the back end of a vt switch */void vt_completechangeconsole (const struct vt *new_console){ unsigned char old_vt_mode; struct vt *old_vt = vtdata.fgconsole; if (new_console == old_vt || (vt_dont_switch)) return; if (!vt_allocated (new_console)) return; last_console = old_vt; /* * If we're switching, we could be going from KD_GRAPHICS to * KD_TEXT mode or vice versa, which means we need to blank or * unblank the screen later. */ old_vt_mode = old_vt->vtd->vc_mode; vt_updatescreen (new_console); /* * If this new console is under process control, send it a signal * telling it that it has acquired. Also check if it has died and * clean up (similar to logic employed in vt_changeconsole()) */ if (new_console->vtd->vt_mode.mode == VT_PROCESS) { /* * Send the signal as privileged - kill_proc() will * tell us if the process has gone or something else * is awry */ if (kill_proc(new_console->vtd->vt_pid, new_console->vtd->vt_mode.acqsig, 1) != 0) { /* * The controlling process has died, so we revert back to * normal operation. In this case, we'll also change back * to KD_TEXT mode. I'm not sure if this is strictly correct * but it saves the agony when the X server dies and the screen * remains blanked due to KD_GRAPHICS! It would be nice to do * this outside of VT_PROCESS but there is no single process * to account for and tracking tty count may be undesirable. */ vt_reset (new_console); } } /* * We do this here because the controlling process above may have * gone, and so there is now a new vc_mode */ if (old_vt_mode != new_console->vtd->vc_mode) { if (new_console->vtd->vc_mode == KD_TEXT) vt_do_unblankscreen (); else { vt_do_blankscreen (1); vcd_setup_graphics (new_console); } } /* Set the colour palette for this VT */ if (new_console->vtd->vc_mode == KD_TEXT) con_set_palette (new_console); /* * Wake anyone waiting for their VT to activate */ vt_wake_waitactive(); return;}/* * Performs the front-end of a vt switch */void vt_changeconsole (struct vt *new_console){ struct vt *old_vt = vtdata.fgconsole; if (new_console == old_vt || (vt_dont_switch)) return; if (!vt_allocated (new_console)) return; /* * If this vt is in process mode, then we need to handshake with * that process before switching. Essentially, we store where that * vt wants to switch to and wait for it to tell us when it's done * (via VT_RELDISP ioctl). * * We also check to see if the controlling process still exists. * If it doesn't, we reset this vt to auto mode and continue. * This is a cheap way to track process control. The worst thing * that can happen is: we send a signal to a process, it dies, and * the switch gets "lost" waiting for a response; hopefully, the * user will try again, we'll detect the process is gone (unless * the user waits just the right amount of time :-) and revert the * vt to auto control. */ if (old_vt->vtd->vt_mode.mode == VT_PROCESS) { /* * Send the signal as privileged - kill_proc() will * tell us if the process has gone or something else * is awry */ if (kill_proc(old_vt->vtd->vt_pid, old_vt->vtd->vt_mode.relsig, 1) == 0) { /* * It worked. Mark the vt to switch to and * return. The process needs to send us a * VT_RELDISP ioctl to complete the switch. */ old_vt->vtd->vt_newvt = new_console; return; } /* * The controlling process has died, so we revert back to * normal operation. In this case, we'll also change back * to KD_TEXT mode. I'm not sure if this is strictly correct * but it saves the agony when the X server dies and the screen * remains blanked due to KD_GRAPHICS! It would be nice to do * this outside of VT_PROCESS but there is no single process * to account for and tracking tty count may be undesirable. */ vt_reset (old_vt); /* * Fall through to normal (VT_AUTO) handling of the switch... */ } /* * Ignore all switches in KD_GRAPHICS+VT_AUTO mode */ if (old_vt->vtd->vc_mode == KD_GRAPHICS) return; vt_completechangeconsole (new_console);}/* * If a vt is under process control, the kernel will not switch to it * immediately, but postpone the operation until the process calls this * ioctl, allowing the switch to complete. * * According to the X sources this is the behavior: * 0: pending switch-from not OK * 1: pending switch-from OK * 2: completed switch-to OK */static inline int vt_reldisp (const struct vt *old_vt, int arg){ int i; if (old_vt->vtd->vt_mode.mode != VT_PROCESS) return -EINVAL; if (old_vt->vtd->vt_newvt) { /* * Switching-from response */ if (arg == 0) /* * Switch disallowed, so forget we were trying * to do it. */ old_vt->vtd->vt_newvt = NULL; else { /* * The current vt has been released, so complete the * switch. */ struct vt *new_vt = old_vt->vtd->vt_newvt; old_vt->vtd->vt_newvt = NULL; i = vt_allocate (new_vt); if (i) return i; vt_completechangeconsole (new_vt); } } else { /* * Switched-to response */ if (arg != VT_ACKACQ) return -EINVAL; } return 0;}/* * Set the mode of a VT. */static inline int vt_kdsetmode (const struct vt *vt, int mode){ /* * Currently, setting the mode from KD_TEXT to KD_GRAPHICS * doesn't do a whole lot. I'm not sure if it should do any * restoration of modes or what... */ switch (mode) { case KD_TEXT0: case KD_TEXT1: mode = KD_TEXT; case KD_GRAPHICS: case KD_TEXT: break; default: return -EINVAL; } if (vt->vtd->vc_mode == mode) return 0; vt->vtd->vc_mode = mode; if (vt != vtdata.fgconsole) return 0; /* * explicitly blank/unblank the screen if switching modes */ if (mode == KD_TEXT) { vt_do_unblankscreen (); vcd_restorestate (vt); } else { vt_do_blankscreen (1); vcd_setup_graphics (vt); } return 0;}static inline int vt_setmode (const struct vt *vt, struct vt_mode *vtmode){ if (vtmode->mode != VT_AUTO && vtmode->mode != VT_PROCESS) return -EINVAL; vt->vtd->vt_mode = *vtmode; vt->vtd->vt_mode.frsig = 0; vt->vtd->vt_pid = current->pid; vt->vtd->vt_newvt = NULL; return 0;}void vt_mksound (unsigned int freq, unsigned int vol, unsigned int ticks){ if (mksound_hook) mksound_hook (freq, vol, ticks);}/* * Deallocate memory associated to VT (but leave VT1) */int vt_deallocate (int arg){ int i; if (arg == 0) { /* * deallocate all unused consoles, but leave 0 */ for (i = 1; i < MAX_NR_CONSOLES; i++) if (!VT_BUSY (i)) vt_disallocate (vt_con_data + i); } else { arg -= 1; if (VT_BUSY (arg)) return -EBUSY; if (arg) vt_disallocate (vt_con_data + arg); } return 0;}int vt_resize (int columns, int rows){ return vcd_resize (rows, columns);}void vt_pokeblankedconsole (void){ timer_active &= ~(1 << BLANK_TIMER); if (vtdata.fgconsole->vtd->vc_mode == KD_GRAPHICS) return; if (vtdata.blanked) { timer_table[BLANK_TIMER].expires = 0; timer_active |= 1 << BLANK_TIMER; } else if (vtdata.screen.blankinterval) { timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval; timer_active |= 1 << BLANK_TIMER; }}static void vt_blankscreen (void);void vt_do_unblankscreen (void){ if (!vtdata.blanked) return; if (!vt_allocated (vtdata.fgconsole)) { /* impossible... */ printk ("unblank_screen: tty %d not allocated ??\n", vtdata.fgconsole->num); return; } timer_table[BLANK_TIMER].fn = vt_blankscreen; if (vtdata.screen.blankinterval) { timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval; timer_active |= 1 << BLANK_TIMER; } vtdata.blanked = NULL; vcd_unblankscreen ();}/* * for panic.c */void do_unblank_screen (void){ vt_do_unblankscreen ();}void vt_do_blankscreen (int nopowersave){ if (vtdata.blanked) return; timer_active &= ~(1 << BLANK_TIMER); timer_table[BLANK_TIMER].fn = do_unblank_screen; vtdata.blanked = vtdata.fgconsole; vcd_blankscreen (nopowersave);}static void vt_blankscreen (void){ vt_do_blankscreen (0);}static int vt_open (struct tty_struct *tty, struct file *filp){ struct vt *vt; unsigned int idx; int i; idx = MINOR(tty->device) - tty->driver.minor_start;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -