I remembered the chvt
(change foreground virtual terminal) command, so that seems
like a good place to start. I looked at BusyBox's implementation in
console-tools/chvt.c
, hoping that it would be small enough to skim through.
At 33 lines, with only 9 lines containing code I wasn't disappointed.
The call to console_make_active
looks interesting. console_make_active
can
be found in libbb/get_console.c
:
xioctl(fd, VT_ACTIVATE, (void *)(ptrdiff_t)vt_num);
That's interesting! We can grep for VT_ACTIVATE
in the Linux kernel to
see how that is handled. (xioctl
is just a small wrapper around ioctl
)
Apart from the declaration in include/uapi/linux/vt.h
, the uses are in
drivers/tty/vt/vt_ioctl.c
. Inside the vt_ioctl
function is where the
actual handling occurs. The permissions and arguments are checked, and then
finally a call to set_console(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--;
console_lock();
ret = vc_allocate(arg);
console_unlock();
if (ret)
return ret;
set_console(arg);
break;
Another step down the rabbit hole, this time ending up at drivers/tty/vt/vt.c
.
This is where the implementation of set_console
is. It sets want_console
to the VT to switch to and calls schedule_console_callback
, which schedules a
console_callback
on a work queue.
int set_console(int nr)
{
...
want_console = nr;
schedule_console_callback();
return 0;
}
The rough call stack for console_callback
would look like:
console_callback ->
change_console ->
complete_change_console ->
switch_screen ->
redraw_screen
redraw_screen
is where fg_console
is actually overwritten to contain the
new virtual terminal number. The above ignores the fact that change_console
might
not call switch_screen
right away, but might send a signal to the process that
controls the current virtual terminal and waits for a VT_RELDISP
ioctl before
switching to the new virtual terminal.
Ok, the internals of set_console
aside, how is it actually used? There's only
a few calls to it, and the ones in drivers/tty/vt/keyboard.c
are likely to be
the interesting ones.
There's 4 uses:
- fn_lastcons, switches to the last used console.
- fn_dec_console, switches to the previous console.
- fn_inc_console, switches to the next console.
- k_cons, switches to console N.
This last use looks like the one we're interested in:
static void k_cons(struct vc_data *vc, unsigned char value, char up_flag)
{
if (up_flag)
return;
set_console(value);
}
This isn't called directly anywhere, it's part of an array:
#define K_HANDLERS\
k_self, k_fn, k_spec, k_pad,\
k_dead, k_cons, k_cur, k_shift,\
k_meta, k_ascii, k_lock, k_lowercase,\
k_slock, k_dead2, k_brl, k_ignore
typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
char up_flag);
static k_handler_fn K_HANDLERS;
static k_handler_fn *k_handler[16] = { K_HANDLERS };
k_handler
is only used in a single place, inside the function kbd_keycode
:
(*k_handler[type])(vc, keysym & 0xff, !down);
Let's see how type
is initialised:
type = KTYP(keysym);
KTYP
is defined on line 46 of include/uapi/linux/keyboard.h
#define KTYP(x) ((x) >> 8)
Just above that is the definition of KT_CONS
.
#define KT_CONS 5
So I searched the tree for usages of KT_CONS
.. but there are none. That's the
only mention of KT_CONS
. Using git log -S KT_CONS
, I looked for commits that
may have added or removed usages of it.
I only found two commits:
commit 607ca46e97a1b6594b29647d98a32d545c24bdff
Author: David Howells <[email protected]>
Date: Sat Oct 13 10:46:48 2012 +0100
UAPI: (Scripted) Disintegrate include/linux
Which is the commit that split include into kernel and userspace APIs.
commit 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (tag: v2.6.12-rc2)
Author: Linus Torvalds <[email protected]>
Date: Sat Apr 16 15:20:36 2005 -0700
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Which is the first commit in the repo, as far back as history will go!
That wasn't much help! So I went back to reading kbd_keycode
more closely.
Aha, the keysym
is found by looking up the keycode
in a key_map
:
if (keycode < NR_KEYS)
keysym = key_map[keycode];
That makes sense, key_map
allows people to remap the keyboard (for customisation,
internationalisation!).
I remember seeing mention of a program loadkeys
, let's see what the manual says:
LOADKEYS(1) General Commands Manual LOADKEYS(1)
NAME
loadkeys - load keyboard translation tables
Ok, this looks like it could be useful!
RESET TO DEFAULT
If the -d (or --default ) option is given, loadkeys loads a default keymap, probably the file defkeymap.map either in /usr/share/keymaps or
in /usr/src/linux/drivers/char. (Probably the former was user-defined, while the latter is a qwerty keyboard map for PCs - maybe not what
was desired.) Sometimes, with a strange keymap loaded (with the minus on some obscure unknown modifier combination) it is easier to type
`loadkeys defkeymap'.
defkeymap.map
, I thought this might be bundled with loadkeys
, but no, it's
actually part of the kernel tree, drivers/tty/vt/defkeymap.map
Looking through this file, you can find the following lines:
keycode 59 = F1 F11 Console_13
control keycode 59 = F1
alt keycode 59 = Console_1
control alt keycode 59 = Console_1
The format is a bit unusal, but not necessary to understand.
I found the source for loadkeys, part of the kbd-project, to try see how the
maps were parsed. After some searching I found Console_1
:
/*
* Keysyms whose KTYP is KT_CONS.
*/
static const char *const cons_syms[] = {
"Console_1",
Not only Console_1
, but KT_CONS
!
Another reference to KT_CONS
:
const syms_entry syms[] = {
E(iso646_syms), /* KT_LATIN */
E(fn_syms), /* KT_FN */
E(spec_syms), /* KT_SPEC */
E(pad_syms), /* KT_PAD */
E(dead_syms), /* KT_DEAD */
E(cons_syms), /* KT_CONS */
E(cons_syms)
is at index 5
, which matches up with the definition of KT_CONS
in Linux's keyboard.h
!
So that's it! They keymap defines keys that send Console_N
key codes that
Linux's tty/VT driver handles directly and changes the current virtual terminal!
How the VT's keyboard driver get the keyboard input is another story..
Linux kernel fc4f28bb3daf3265d6bc5f73b497306985bb23ab