// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 1992 obz under the linux copyright * * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993 * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994 * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995 * Some code moved for less code duplication - Andi Kleen - Mar 1997 * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001
*/
/* * console_lock must be held to prevent the vc from being deallocated * while we're checking whether it's in-use.
*/
WARN_CONSOLE_UNLOCKED();
return vc && kref_read(&vc->port.kref) > 1;
}
staticinlinebool vt_busy(int i)
{ if (vt_in_use(i)) returntrue; if (i == fg_console) returntrue; if (vc_is_sel(vc_cons[i].d)) returntrue;
returnfalse;
}
/* * Console (vt and kd) routines, as defined by USL SVR4 manual, and by * experimentation and study of X386 SYSV handling. * * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console, * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing * to the current console is done by the main ioctl code.
*/
/** * vt_event_post * @event: the event that occurred * @old: old console * @new: new console * * Post an VT event to interested VT handlers
*/
void vt_event_post(unsignedint event, unsignedint old, unsignedintnew)
{ struct list_head *pos, *head; unsignedlong flags; int wake = 0;
spin_lock_irqsave(&vt_event_lock, flags);
head = &vt_events;
list_for_each(pos, head) { struct vt_event_wait *ve = list_entry(pos, struct vt_event_wait, list); if (!(ve->event.event & event)) continue;
ve->event.event = event; /* kernel view is consoles 0..n-1, user space view is
console 1..n with 0 meaning current, so we must bias */
ve->event.oldev = old + 1;
ve->event.newev = new + 1;
wake = 1;
ve->done = 1;
}
spin_unlock_irqrestore(&vt_event_lock, flags); if (wake)
wake_up_interruptible(&vt_event_waitqueue);
}
/* Dequeue it */
spin_lock_irqsave(&vt_event_lock, flags);
list_del(&vw->list);
spin_unlock_irqrestore(&vt_event_lock, flags);
}
/** * vt_event_wait - wait for an event * @vw: our event * * Waits for an event to occur which completes our vt_event_wait * structure. On return the structure has wv->done set to 1 for success * or 0 if some event such as a signal ended the wait.
*/
if (copy_from_user(&vw.event, event, sizeof(struct vt_event))) return -EFAULT; /* Highest supported event for now */ if (vw.event.event & ~VT_MAX_EVENT) return -EINVAL;
vt_event_wait(&vw); /* If it occurred report it */ if (vw.done) { if (copy_to_user(event, &vw.event, sizeof(struct vt_event))) return -EFAULT; return 0;
} return -EINTR;
}
/** * vt_waitactive - active console wait * @n: new console * * Helper for event waits. Used to implement the legacy * event waiting ioctls in terms of events
*/
int vt_waitactive(int n)
{ struct vt_event_wait vw; do {
vw.event.event = VT_EVENT_SWITCH;
__vt_event_queue(&vw); if (n == fg_console + 1) {
__vt_event_dequeue(&vw); break;
}
__vt_event_wait(&vw);
__vt_event_dequeue(&vw); if (vw.done == 0) return -EINTR;
} while (vw.event.newev != n); return 0;
}
/* * these are the valid i/o ports we're allowed to change. they map all the * video ports
*/ #define GPFIRST 0x3b4 #define GPLAST 0x3df #define GPNUM (GPLAST - GPFIRST + 1)
/* * 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... * * XXX It should at least call into the driver, fbdev's definitely need to * restore their engine state. --BenH * * Called with the console lock held.
*/ staticint vt_kdsetmode(struct vc_data *vc, unsignedlong mode)
{ switch (mode) { case KD_GRAPHICS: break; case KD_TEXT0: case KD_TEXT1:
mode = KD_TEXT;
fallthrough; case KD_TEXT: break; default: return -EINVAL;
}
if (vc->vc_mode == mode) return 0;
vc->vc_mode = mode; if (vc->vc_num != fg_console) return 0;
/* explicitly blank/unblank the screen if switching modes */ if (mode == KD_TEXT)
do_unblank_screen(1); else
do_blank_screen(1);
switch (cmd) { case KIOCSOUND: if (!perm) return -EPERM; /* * The use of PIT_TICK_RATE is historic, it used to be * the platform-dependent CLOCK_TICK_RATE between 2.6.12 * and 2.6.36, which was a minor but unfortunate ABI * change. kd_mksound is locked by the input layer.
*/ if (arg)
arg = PIT_TICK_RATE / arg;
kd_mksound(arg, 0); break;
case KDMKTONE: if (!perm) return -EPERM;
{ unsignedint ticks, count;
/* * Generate the tone for the appropriate number of ticks. * If the time is zero, turn off sound ourselves.
*/
ticks = msecs_to_jiffies((arg >> 16) & 0xffff);
count = ticks ? (arg & 0xffff) : 0; if (count)
count = PIT_TICK_RATE / count;
kd_mksound(count, ticks); break;
}
case KDGKBTYPE: /* * this is naïve.
*/ return put_user(KB_101, (char __user *)arg);
/* * These cannot be implemented on any machine that implements * ioperm() in user level (such as Alpha PCs) or not at all. * * XXX: you should never use these, just call ioperm directly..
*/ #ifdef CONFIG_X86 case KDADDIO: case KDDELIO: /* * KDADDIO and KDDELIO may be able to add ports beyond what * we reject here, but to be safe... * * These are locked internally via sys_ioperm
*/ if (arg < GPFIRST || arg > GPLAST) return -EINVAL;
case KDENABIO: case KDDISABIO: return ksys_ioperm(GPFIRST, GPNUM,
(cmd == KDENABIO)) ? -ENXIO : 0; #endif
/* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */
case KDKBDREP:
{ struct kbd_repeat kbrep;
if (!capable(CAP_SYS_TTY_CONFIG)) return -EPERM;
if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) return -EFAULT;
ret = kbd_rate(&kbrep); if (ret) return ret; if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat))) return -EFAULT; break;
}
case KDSETMODE: if (!perm) return -EPERM;
console_lock();
ret = vt_kdsetmode(vc, arg);
console_unlock(); return ret;
case KDGETMODE: return put_user(vc->vc_mode, (int __user *)arg);
case KDMAPDISP: case KDUNMAPDISP: /* * these work like a combination of mmap and KDENABIO. * this could be easily finished.
*/ return -EINVAL;
case KDSKBMODE: if (!perm) return -EPERM;
ret = vt_do_kdskbmode(console, arg); if (ret) return ret;
tty_ldisc_flush(tty); break;
case KDGKBMODE: return put_user(vt_do_kdgkbmode(console), (int __user *)arg);
/* this could be folded into KDSKBMODE, but for compatibility
reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */ case KDSKBMETA: return vt_do_kdskbmeta(console, arg);
case KDGKBMETA: /* FIXME: should review whether this is worth locking */ return put_user(vt_do_kdgkbmeta(console), (int __user *)arg);
case KDGETKEYCODE: case KDSETKEYCODE: if(!capable(CAP_SYS_TTY_CONFIG))
perm = 0; return vt_do_kbkeycode_ioctl(cmd, up, perm);
case KDGKBENT: case KDSKBENT: return vt_do_kdsk_ioctl(cmd, up, perm, console);
case KDGKBSENT: case KDSKBSENT: return vt_do_kdgkb_ioctl(cmd, up, perm);
/* Diacritical processing. Handled in keyboard.c as it has
to operate on the keyboard locks and structures */ case KDGKBDIACR: case KDGKBDIACRUC: case KDSKBDIACR: case KDSKBDIACRUC: return vt_do_diacrit(cmd, up, perm);
/* the ioctls below read/set the flags usually shown in the leds */ /* don't use them - they will go away without warning */ case KDGKBLED: case KDSKBLED: case KDGETLED: case KDSETLED: return vt_do_kdskled(console, cmd, arg, perm);
/* * A process can indicate its willingness to accept signals * generated by pressing an appropriate key combination. * Thus, one can have a daemon that e.g. spawns a new console * upon a keypress and then changes to it. * See also the kbrequest field of inittab(5).
*/ case KDSIGACCEPT: if (!perm || !capable(CAP_KILL)) return -EPERM; if (!valid_signal(arg) || arg < 1 || arg == SIGKILL) return -EINVAL;
case PIO_SCRNMAP: if (!perm) return -EPERM; return con_set_trans_old(up);
case GIO_SCRNMAP: return con_get_trans_old(up);
case PIO_UNISCRNMAP: if (!perm) return -EPERM; return con_set_trans_new(up);
case GIO_UNISCRNMAP: return con_get_trans_new(up);
case PIO_UNIMAPCLR: if (!perm) return -EPERM;
con_clear_unimap(vc); break;
case PIO_UNIMAP: case GIO_UNIMAP: return do_unimap_ioctl(cmd, up, perm, vc);
default: return -ENOIOCTLCMD;
}
return 0;
}
staticint vt_reldisp(struct vc_data *vc, unsignedint swtch)
{ int newvt, ret;
if (vc->vt_mode.mode != VT_PROCESS) return -EINVAL;
/* Switched-to response */ if (vc->vt_newvt < 0) { /* If it's just an ACK, ignore it */ return swtch == VT_ACKACQ ? 0 : -EINVAL;
}
/* Switching-from response */ if (swtch == 0) { /* Switch disallowed, so forget we were trying to do it. */
vc->vt_newvt = -1; return 0;
}
/* The current vt has been released, so complete the switch. */
newvt = vc->vt_newvt;
vc->vt_newvt = -1;
ret = vc_allocate(newvt); if (ret) return ret;
/* * When we actually do the console switch, make sure we are atomic with * respect to other console switches..
*/
complete_change_console(vc_cons[newvt].d);
if (copy_from_user(&vsa, sa, sizeof(vsa))) return -EFAULT; if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) return -ENXIO;
vsa.console--;
vsa.console = array_index_nospec(vsa.console, MAX_NR_CONSOLES);
console_lock();
ret = vc_allocate(vsa.console); if (ret) {
console_unlock(); return ret;
}
/* * This is safe providing we don't drop the console sem between * vc_allocate and finishing referencing nvc.
*/
nvc = vc_cons[vsa.console].d;
nvc->vt_mode = vsa.mode;
nvc->vt_mode.frsig = 0;
put_pid(nvc->vt_pid);
nvc->vt_pid = get_pid(task_pid(current));
console_unlock();
if (copy_from_user(&v, cs, sizeof(struct vt_consize))) return -EFAULT;
/* FIXME: Should check the copies properly */ if (!v.v_vlin)
v.v_vlin = vc->vc_scan_lines;
if (v.v_clin) { int rows = v.v_vlin / v.v_clin; if (v.v_rows != rows) { if (v.v_rows) /* Parameters don't add up */ return -EINVAL;
v.v_rows = rows;
}
}
if (v.v_vcol && v.v_ccol) { int cols = v.v_vcol / v.v_ccol; if (v.v_cols != cols) { if (v.v_cols) return -EINVAL;
v.v_cols = cols;
}
}
if (v.v_clin > 32) return -EINVAL;
for (i = 0; i < MAX_NR_CONSOLES; i++) { struct vc_data *vcp;
if (!vc_cons[i].d) continue;
console_lock();
vcp = vc_cons[i].d; if (vcp) { int ret; int save_scan_lines = vcp->vc_scan_lines; int save_cell_height = vcp->vc_cell_height;
if (v.v_vlin)
vcp->vc_scan_lines = v.v_vlin; if (v.v_clin)
vcp->vc_cell_height = v.v_clin;
ret = __vc_resize(vcp, v.v_cols, v.v_rows, true); if (ret) {
vcp->vc_scan_lines = save_scan_lines;
vcp->vc_cell_height = save_cell_height;
console_unlock(); return ret;
}
}
console_unlock();
}
return 0;
}
/* * We handle the console-specific ioctl's here. We allow the * capability to modify any console, not just the fg_console.
*/ int vt_ioctl(struct tty_struct *tty, unsignedint cmd, unsignedlong arg)
{ struct vc_data *vc = tty->driver_data; void __user *up = (void __user *)arg; int i, perm; int ret;
/* * To have permissions to do most of the vt ioctls, we either have * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
*/
perm = 0; if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG))
perm = 1;
ret = vt_k_ioctl(tty, cmd, arg, perm); if (ret != -ENOIOCTLCMD) return ret;
ret = vt_io_ioctl(vc, cmd, up, perm); if (ret != -ENOIOCTLCMD) return ret;
switch (cmd) { case TIOCLINUX: return tioclinux(tty, arg); case VT_SETMODE:
{ struct vt_mode tmp;
if (!perm) return -EPERM; if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) return -EFAULT; if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) return -EINVAL;
console_lock();
vc->vt_mode = tmp; /* the frsig is ignored, so we set it to 0 */
vc->vt_mode.frsig = 0;
put_pid(vc->vt_pid);
vc->vt_pid = get_pid(task_pid(current)); /* no switch is required -- saw@shade.msu.ru */
vc->vt_newvt = -1;
console_unlock(); break;
}
/* * Returns global vt state. Note that VT 0 is always open, since * it's an alias for the current VT, and people can't use it here. * We cannot return state for more than 16 VTs, since v_state is short.
*/ case VT_GETSTATE:
{ struct vt_stat __user *vtstat = up; unsignedshort state, mask;
if (put_user(fg_console + 1, &vtstat->v_active)) return -EFAULT;
state = 1; /* /dev/tty0 is always open */
console_lock(); /* required by vt_in_use() */ for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask;
++i, mask <<= 1) if (vt_in_use(i))
state |= mask;
console_unlock(); return put_user(state, &vtstat->v_state);
}
/* * Returns the first available (non-opened) console.
*/ case VT_OPENQRY:
console_lock(); /* required by vt_in_use() */ for (i = 0; i < MAX_NR_CONSOLES; ++i) if (!vt_in_use(i)) break;
console_unlock();
i = i < MAX_NR_CONSOLES ? (i+1) : -1; return put_user(i, (int __user *)arg);
/* * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num, * with num >= 1 (switches to vt 0, our console, are not allowed, just * to preserve sanity).
*/ case VT_ACTIVATE: if (!perm) return -EPERM; if (arg == 0 || arg > MAX_NR_CONSOLES) return -ENXIO;
arg--;
arg = array_index_nospec(arg, MAX_NR_CONSOLES);
console_lock();
ret = vc_allocate(arg);
console_unlock(); if (ret) return ret;
set_console(arg); break;
case VT_SETACTIVATE: if (!perm) return -EPERM;
return vt_setactivate(up);
/* * wait until the specified VT has been activated
*/ case VT_WAITACTIVE: if (!perm) return -EPERM; if (arg == 0 || arg > MAX_NR_CONSOLES) return -ENXIO; return vt_waitactive(arg);
/* * 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
*/ case VT_RELDISP: if (!perm) return -EPERM;
console_lock();
ret = vt_reldisp(vc, arg);
console_unlock();
return ret;
/* * Disallocate memory associated to VT (but leave VT1)
*/ case VT_DISALLOCATE: if (arg > MAX_NR_CONSOLES) return -ENXIO;
console_lock();
vc = vc_con->d; if (vc) { /* FIXME: review tty ref counting */
tty = vc->port.tty; /* * SAK should also work in all raw modes and reset * them properly.
*/ if (tty)
__do_SAK(tty);
reset_vc(vc);
}
console_unlock();
}
#ifdef CONFIG_COMPAT
struct compat_console_font_op {
compat_uint_t op; /* operation code KD_FONT_OP_* */
compat_uint_t flags; /* KD_FONT_FLAG_* */
compat_uint_t width, height; /* font size */
compat_uint_t charcount;
compat_caddr_t data; /* font data with height fixed to 32 */
};
staticinlineint
compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, int perm, struct console_font_op *op, struct vc_data *vc)
{ int i;
if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op))) return -EFAULT; if (!perm && op->op != KD_FONT_OP_GET) return -EPERM;
op->data = compat_ptr(((struct compat_console_font_op *)op)->data);
i = con_font_op(vc, op); if (i) return i;
((struct compat_console_font_op *)op)->data = (unsignedlong)op->data; if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op))) return -EFAULT; return 0;
}
if (copy_from_user(&tmp, user_ud, sizeof tmp)) return -EFAULT;
tmp_entries = compat_ptr(tmp.entries); switch (cmd) { case PIO_UNIMAP: if (!perm) return -EPERM; return con_set_unimap(vc, tmp.entry_ct, tmp_entries); case GIO_UNIMAP: if (!perm && fg_console != vc->vc_num) return -EPERM; return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries);
} return 0;
}
long vt_compat_ioctl(struct tty_struct *tty, unsignedint cmd, unsignedlong arg)
{ struct vc_data *vc = tty->driver_data; struct console_font_op op; /* used in multiple places here */ void __user *up = compat_ptr(arg); int perm;
/* * To have permissions to do most of the vt ioctls, we either have * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
*/
perm = 0; if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG))
perm = 1;
switch (cmd) { /* * these need special handlers for incompatible data structures
*/
case KDFONTOP: return compat_kdfontop_ioctl(up, perm, &op, vc);
case PIO_UNIMAP: case GIO_UNIMAP: return compat_unimap_ioctl(cmd, up, perm, vc);
/* * all these treat 'arg' as an integer
*/ case KIOCSOUND: case KDMKTONE: #ifdef CONFIG_X86 case KDADDIO: case KDDELIO: #endif case KDSETMODE: case KDMAPDISP: case KDUNMAPDISP: case KDSKBMODE: case KDSKBMETA: case KDSKBLED: case KDSETLED: case KDSIGACCEPT: case VT_ACTIVATE: case VT_WAITACTIVE: case VT_RELDISP: case VT_DISALLOCATE: return vt_ioctl(tty, cmd, arg);
/* * the rest has a compatible data structure behind arg, * but we have to convert it to a proper 64 bit pointer.
*/ default: return vt_ioctl(tty, cmd, (unsignedlong)up);
}
}
#endif/* CONFIG_COMPAT */
/* * Performs the back end of a vt switch. Called under the console * semaphore.
*/ staticvoid complete_change_console(struct vc_data *vc)
{ unsignedchar old_vc_mode; int old = fg_console;
last_console = fg_console;
/* * 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_vc_mode = vc_cons[fg_console].d->vc_mode;
switch_screen(vc);
/* * This can't appear below a successful kill_pid(). If it did, * then the *blank_screen operation could occur while X, having * received acqsig, is waking up on another processor. This * condition can lead to overlapping accesses to the VGA range * and the framebuffer (causing system lockups). * * To account for this we duplicate this code below only if the * controlling process is gone and we've called reset_vc.
*/ if (old_vc_mode != vc->vc_mode) { if (vc->vc_mode == KD_TEXT)
do_unblank_screen(1); else
do_blank_screen(1);
}
/* * 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 change_console())
*/ if (vc->vt_mode.mode == VT_PROCESS) { /* * Send the signal as privileged - kill_pid() will * tell us if the process has gone or something else * is awry
*/ if (kill_pid(vc->vt_pid, vc->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.
*/
reset_vc(vc);
if (old_vc_mode != vc->vc_mode) { if (vc->vc_mode == KD_TEXT)
do_unblank_screen(1); else
do_blank_screen(1);
}
}
}
/* * Wake anyone waiting for their VT to activate
*/
vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num); return;
}
/* * Performs the front-end of a vt switch
*/ void change_console(struct vc_data *new_vc)
{ struct vc_data *vc;
if (!new_vc || new_vc->vc_num == fg_console || vt_dont_switch) 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.
*/
vc = vc_cons[fg_console].d; if (vc->vt_mode.mode == VT_PROCESS) { /* * Send the signal as privileged - kill_pid() will * tell us if the process has gone or something else * is awry. * * We need to set vt_newvt *before* sending the signal or we * have a race.
*/
vc->vt_newvt = new_vc->vc_num; if (kill_pid(vc->vt_pid, vc->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.
*/ 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.
*/
reset_vc(vc);
/* * Fall through to normal (VT_AUTO) handling of the switch...
*/
}
/* * Ignore all switches in KD_GRAPHICS+VT_AUTO mode
*/ if (vc->vc_mode == KD_GRAPHICS) return;
complete_change_console(new_vc);
}
/* Perform a kernel triggered VT switch for suspend/resume */
staticint disable_vt_switch;
int vt_move_to_console(unsignedint vt, int alloc)
{ int prev;
console_lock(); /* Graphics mode - up to X */ if (disable_vt_switch) {
console_unlock(); return 0;
}
prev = fg_console;
if (alloc && vc_allocate(vt)) { /* we can't have a free VC for now. Too bad,
* we don't want to mess the screen for now. */
console_unlock(); return -ENOSPC;
}
if (set_console(vt)) { /* * We're unable to switch to the SUSPEND_CONSOLE. * Let the calling function know so it can decide * what to do.
*/
console_unlock(); return -EIO;
}
console_unlock(); if (vt_waitactive(vt + 1)) {
pr_debug("Suspend: Can't switch VCs."); return -EINTR;
} return prev;
}
/* * Normally during a suspend, we allocate a new console and switch to it. * When we resume, we switch back to the original console. This switch * can be slow, so on systems where the framebuffer can handle restoration * of video registers anyways, there's little point in doing the console * switch. This function allows you to disable it by passing it '0'.
*/ void pm_set_vt_switch(int do_switch)
{
console_lock();
disable_vt_switch = !do_switch;
console_unlock();
}
EXPORT_SYMBOL(pm_set_vt_switch);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.