[pci] Add support for PCI MSI-X interrupts
[ipxe.git] / src / util / genkeymap.pl
1 #!/usr/bin/perl -w
2 #
3 # Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; either version 2 of the
8 # License, or any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19
20 =head1 NAME
21
22 genkeymap.pl
23
24 =head1 SYNOPSIS
25
26 genkeymap.pl [options] <keymap name>
27
28 Options:
29
30 -f,--from=<name> Set BIOS keymap name (default "us")
31 -h,--help Display brief help message
32 -v,--verbose Increase verbosity
33 -q,--quiet Decrease verbosity
34
35 =cut
36
37 # With reference to:
38 #
39 # http://gunnarwrobel.de/wiki/Linux-and-the-keyboard.html
40
41 use Getopt::Long;
42 use Pod::Usage;
43 use strict;
44 use warnings;
45
46 use constant BIOS_KEYMAP => "us";
47 use constant BKEYMAP_MAGIC => "bkeymap";
48 use constant MAX_NR_KEYMAPS => 256;
49 use constant NR_KEYS => 128;
50 use constant KG_SHIFT => 0;
51 use constant KG_ALTGR => 1;
52 use constant KG_CTRL => 2;
53 use constant KG_ALT => 3;
54 use constant KG_SHIFTL => 4;
55 use constant KG_KANASHIFT => 4;
56 use constant KG_SHIFTR => 5;
57 use constant KG_CTRLL => 6;
58 use constant KG_CTRLR => 7;
59 use constant KG_CAPSSHIFT => 8;
60 use constant KT_LATIN => 0;
61 use constant KT_FN => 1;
62 use constant KT_SPEC => 2;
63 use constant KT_PAD => 3;
64 use constant KT_DEAD => 4;
65 use constant KT_CONS => 5;
66 use constant KT_CUR => 6;
67 use constant KT_SHIFT => 7;
68 use constant KT_META => 8;
69 use constant KT_ASCII => 9;
70 use constant KT_LOCK => 10;
71 use constant KT_LETTER => 11;
72 use constant KT_SLOCK => 12;
73 use constant KT_SPKUP => 14;
74
75 my $verbosity = 1;
76 my $from_name = BIOS_KEYMAP;
77
78 # Read named keymaps using "loadkeys -b"
79 #
80 sub read_keymaps {
81 my $name = shift;
82 my $keymaps = [];
83
84 # Generate binary keymap
85 open my $pipe, "-|", "loadkeys", "-b", $name
86 or die "Could not load keymap \"".$name."\": $!\n";
87
88 # Check magic
89 read $pipe, my $magic, length BKEYMAP_MAGIC
90 or die "Could not read from \"".$name."\": $!\n";
91 die "Bad magic value from \"".$name."\"\n"
92 unless $magic eq BKEYMAP_MAGIC;
93
94 # Read list of included keymaps
95 read $pipe, my $included, MAX_NR_KEYMAPS
96 or die "Could not read from \"".$name."\": $!\n";
97 my @included = unpack ( "C*", $included );
98 die "Missing or truncated keymap list from \"".$name."\"\n"
99 unless @included == MAX_NR_KEYMAPS;
100
101 # Read each keymap in turn
102 for ( my $keymap = 0 ; $keymap < MAX_NR_KEYMAPS ; $keymap++ ) {
103 if ( $included[$keymap] ) {
104 read $pipe, my $keysyms, ( NR_KEYS * 2 )
105 or die "Could not read from \"".$name."\": $!\n";
106 my @keysyms = unpack ( "S*", $keysyms );
107 die "Missing or truncated keymap ".$keymap." from \"".$name."\"\n"
108 unless @keysyms == NR_KEYS;
109 push @$keymaps, \@keysyms;
110 } else {
111 push @$keymaps, undef;
112 }
113 }
114
115 close $pipe;
116 return $keymaps;
117 }
118
119 # Translate keysym value to ASCII
120 #
121 sub keysym_to_ascii {
122 my $keysym = shift;
123
124 # Non-existent keysyms have no ASCII equivalent
125 return unless $keysym;
126
127 # Sanity check
128 if ( $keysym & 0xf000 ) {
129 warn "Unexpected keysym ".sprintf ( "0x%04x", $keysym )."\n";
130 return;
131 }
132
133 # Extract type and value
134 my $type = ( $keysym >> 8 );
135 my $value = ( $keysym & 0xff );
136
137 # Non-simple types have no ASCII equivalent
138 return unless ( ( $type == KT_LATIN ) || ( $type == KT_ASCII ) ||
139 ( $type == KT_LETTER ) );
140
141 # High-bit-set characters cannot be generated on a US keyboard
142 return if $value & 0x80;
143
144 return $value;
145 }
146
147 # Translate ASCII to descriptive name
148 #
149 sub ascii_to_name {
150 my $ascii = shift;
151
152 if ( $ascii == 0x5c ) {
153 return "'\\\\'";
154 } elsif ( $ascii == 0x27 ) {
155 return "'\\\''";
156 } elsif ( ( $ascii >= 0x20 ) && ( $ascii <= 0x7e ) ) {
157 return sprintf ( "'%c'", $ascii );
158 } elsif ( $ascii <= 0x1a ) {
159 return sprintf ( "Ctrl-%c", ( 0x40 + $ascii ) );
160 } else {
161 return sprintf ( "0x%02x", $ascii );
162 }
163 }
164
165 # Produce translation table between two keymaps
166 #
167 sub translate_keymaps {
168 my $from = shift;
169 my $to = shift;
170 my $map = {};
171
172 foreach my $keymap ( 0, 1 << KG_SHIFT, 1 << KG_CTRL ) {
173 for ( my $keycode = 0 ; $keycode < NR_KEYS ; $keycode++ ) {
174 my $from_ascii = keysym_to_ascii ( $from->[$keymap]->[$keycode] )
175 or next;
176 my $to_ascii = keysym_to_ascii ( $to->[$keymap]->[$keycode] )
177 or next;
178 my $new_map = ( ! exists $map->{$from_ascii} );
179 my $update_map =
180 ( $new_map || ( $keycode < $map->{$from_ascii}->{keycode} ) );
181 if ( ( $verbosity > 1 ) &&
182 ( ( $from_ascii != $to_ascii ) ||
183 ( $update_map && ! $new_map ) ) ) {
184 printf STDERR "In keymap %d: %s => %s%s\n", $keymap,
185 ascii_to_name ( $from_ascii ), ascii_to_name ( $to_ascii ),
186 ( $update_map ? ( $new_map ? "" : " (override)" )
187 : " (ignored)" );
188 }
189 if ( $update_map ) {
190 $map->{$from_ascii} = {
191 to_ascii => $to_ascii,
192 keycode => $keycode,
193 };
194 }
195 }
196 }
197 return { map { $_ => $map->{$_}->{to_ascii} } keys %$map };
198 }
199
200 # Parse command-line options
201 Getopt::Long::Configure ( 'bundling', 'auto_abbrev' );
202 GetOptions (
203 'verbose|v+' => sub { $verbosity++; },
204 'quiet|q+' => sub { $verbosity--; },
205 'from|f=s' => sub { shift; $from_name = shift; },
206 'help|h' => sub { pod2usage ( 1 ); },
207 ) or die "Could not parse command-line options\n";
208 pod2usage ( 1 ) unless @ARGV == 1;
209 my $to_name = shift;
210
211 # Read and translate keymaps
212 my $from = read_keymaps ( $from_name );
213 my $to = read_keymaps ( $to_name );
214 my $map = translate_keymaps ( $from, $to );
215
216 # Generate output
217 ( my $to_name_c = $to_name ) =~ s/\W/_/g;
218 printf "/** \@file\n";
219 printf " *\n";
220 printf " * \"".$to_name."\" keyboard mapping\n";
221 printf " *\n";
222 printf " * This file is automatically generated; do not edit\n";
223 printf " *\n";
224 printf " */\n";
225 printf "\n";
226 printf "FILE_LICENCE ( PUBLIC_DOMAIN );\n";
227 printf "\n";
228 printf "#include <ipxe/keymap.h>\n";
229 printf "\n";
230 printf "/** \"".$to_name."\" keyboard mapping */\n";
231 printf "struct key_mapping ".$to_name_c."_mapping[] __keymap = {\n";
232 foreach my $from_sym ( sort { $a <=> $b } keys %$map ) {
233 my $to_sym = $map->{$from_sym};
234 next if $from_sym == $to_sym;
235 printf "\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n", $from_sym, $to_sym,
236 ascii_to_name ( $from_sym ), ascii_to_name ( $to_sym );
237 }
238 printf "};\n";