1.08
[platform/upstream/kbd.git] / src / kbdrate.c
1 /*
2 From: faith@cs.unc.edu (Rik Faith)
3 Subject: User mode keyboard rate changer
4 Date: 27 Apr 92 13:44:26 GMT
5
6 I put together this program, called kbdrate.c, which will reset the keyboard
7 repeat rate and delay in user mode.  The program must have read/write
8 access to /dev/port, so if /dev/port is only read/writeable by group port,
9 then kbdrate must run setgid to group port (for example).
10
11 The "rate" is the rate in characters per second
12
13 The "delay" is the amount of time the key must remain depressed before it
14 will start to repeat.
15
16 Usage examples:
17
18 kbdrate                 set rate to IBM default (10.9 cps, 250ms delay)
19 kbdrate -r 30.0         set rate to 30 cps and delay to 250ms
20 kbdrate -r 20.0 -s      set rate to 20 cps (delay 250ms) -- don't print message
21 kbdrate -r 0 -d 0       set rate to 2.0 cps and delay to 250 ms
22
23 I find it useful to put kbdrate in my /etc/rc file so that the keyboard
24 rate is set to something that I find comfortable at boot time.  This sure
25 beats rebuilding the kernel!
26
27
28   kbdrate.c -- Set keyboard typematic rate (and delay)
29   Created: Thu Apr 23 12:24:30 1992
30   Author: Rickard E. Faith, faith@cs.unc.edu
31
32   Copyright 1992 Rickard E. Faith.  Distributed under the GPL.
33   This program comes with ABSOLUTELY NO WARRANTY.
34   Usage: kbdrate [-r rate] [-d delay] [-s]
35          Rate can range from 2.0 to 30.0 (units are characters per second)
36          Delay can range from 250 to 1000 (units are milliseconds)
37          -s suppressed message
38   Compiles under gcc 2.1 for Linux (tested with the pre-0.96 kernel)
39  
40   Wed Jun 22 21:35:43 1994, faith@cs.unc.edu:
41             Changed valid_rates per suggestion by Andries.Brouwer@cwi.nl.
42   Wed Jun 22 22:18:29 1994, faith@cs.unc.edu:
43             Added patch for AUSTIN notebooks from John Bowman
44             (bowman@hagar.ph.utexas.edu)
45  
46   Linux/68k modifications by Roman Hodek 
47                                 (Roman.Hodek@informatik.uni-erlangen.de):
48  
49   Reading/writing the Intel I/O ports via /dev/port is not the
50   English way... Such hardware dependent stuff can never work on
51   other architectures.
52   
53   Linux/68k has an new ioctl for setting the keyboard repeat rate
54   and delay. Both values are counted in msecs, the kernel will do
55   any rounding to values possible with the underlying hardware.
56  
57   kbdrate now first tries if the KDKBDREP ioctl is available. If it
58   is, it is used, else the old method is applied.
59
60   1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
61   - added Native Language Support
62
63   1999-03-17
64   Linux/SPARC modifications by Jeffrey Connell <ankh@canuck.gen.nz>:
65   It seems that the KDKBDREP ioctl is not available on this platform.
66   However, Linux/SPARC has its own ioctl for this, with yet another
67   different measurement system.  Thus, try for KIOCSRATE, too.
68
69 */
70
71 #include <stdio.h>
72 #include <unistd.h>
73 #include <stdlib.h>
74 #include <errno.h>
75 #include <sys/file.h>
76 #include <sys/ioctl.h>
77
78 #ifdef __sparc__
79 #include <asm/param.h>
80 #include <asm/kbio.h>
81 #endif
82
83 #ifndef KDKBDREP
84 /* usually defined in <linux/kd.h> */
85 #define KDKBDREP        0x4B52  /* set keyboard delay/repeat rate;
86                                  * actually used values are returned */
87 struct kbd_repeat {
88         int delay;      /* in msec; <= 0: don't change */
89         int period;       /* in msec; <= 0: don't change */
90 };
91 #endif
92
93 #include "nls.h"
94 #include "version.h"
95
96 static int valid_rates[] = { 300, 267, 240, 218, 200, 185, 171, 160, 150,
97                                    133, 120, 109, 100, 92, 86, 80, 75, 67,
98                                    60, 55, 50, 46, 43, 40, 37, 33, 30, 27,
99                                    25, 23, 21, 20 };
100 #define RATE_COUNT (sizeof( valid_rates ) / sizeof( int ))
101
102 static int valid_delays[] = { 250, 500, 750, 1000 };
103 #define DELAY_COUNT (sizeof( valid_delays ) / sizeof( int ))
104
105 static int
106 KDKBDREP_ioctl_ok(double rate, int delay, int silent) {
107         /* This ioctl is defined in <linux/kd.h> but is not
108            implemented anywhere - must be in some m68k patches. */
109         struct kbd_repeat kbdrep_s;
110
111         /* don't change, just test */
112         kbdrep_s.period = -1;
113         kbdrep_s.delay = -1;
114         if (ioctl( 0, KDKBDREP, &kbdrep_s )) {
115                 if (errno == EINVAL)
116                         return 0;
117                 perror( "ioctl(KDKBDREP)" );
118                 exit( 1 );
119         }
120
121 #if 0
122         printf("old delay %d, period %d\n",
123                kbdrep_s.delay, kbdrep_s.period);
124 #endif
125
126         /* do the change */
127         if (rate == 0)                            /* switch repeat off */
128                 kbdrep_s.period = 0;
129         else
130                 kbdrep_s.period  = 1000.0 / rate; /* convert cps to msec */
131         if (kbdrep_s.period < 1)
132                 kbdrep_s.period = 1;
133         kbdrep_s.delay = delay;
134         if (kbdrep_s.delay < 1)
135                 kbdrep_s.delay = 1;
136    
137         if (ioctl(0, KDKBDREP, &kbdrep_s)) {
138                 perror("ioctl(KDKBDREP)");
139                 exit(1);
140         }
141
142         /* report */
143         if (kbdrep_s.period == 0)
144                 rate = 0;
145         else
146                 rate = 1000.0 / (double) kbdrep_s.period;
147
148         if (!silent)
149                 printf( _("Typematic Rate set to %.1f cps (delay = %d ms)\n"),
150                         rate, kbdrep_s.delay );
151
152         return 1;                       /* success! */
153 }
154
155 static int
156 KIOCSRATE_ioctl_ok(double rate, int delay, int silent) {
157 #ifdef KIOCSRATE
158         struct kbd_rate kbdrate_s;
159         int fd;
160
161         fd = open("/dev/kbd", O_RDONLY);
162         if (fd == -1) {
163                 perror( "open(/dev/kbd)" );
164                 exit( 1 );
165         }
166
167         kbdrate_s.period = (int) (rate + 0.5);  /* round up */
168         kbdrate_s.delay = delay * HZ / 1000;  /* convert ms to Hz */
169         if (kbdrate_s.period > 50)
170                 kbdrate_s.period = 50;
171
172         if (ioctl( fd, KIOCSRATE, &kbdrate_s )) {
173                 perror( "ioctl(KIOCSRATE)" );
174                 exit( 1 );
175         }
176         close( fd );
177
178         if (!silent)
179                 printf( "Typematic Rate set to %d cps (delay = %d ms)\n",
180                         kbdrate_s.period, kbdrate_s.delay * 1000 / HZ );
181
182         return 1;
183 #else /* no KIOCSRATE */
184         return 0;
185 #endif /* KIOCSRATE */
186 }
187
188 int
189 main( int argc, char **argv ) {
190 #ifdef __sparc__
191         double      rate = 5.0;      /* Default rate */
192         int         delay = 200;     /* Default delay */
193 #else
194         double      rate = 10.9;     /* Default rate */
195         int         delay = 250;     /* Default delay */
196 #endif
197         int         value = 0x7f;    /* Maximum delay with slowest rate */
198                                      /* DO NOT CHANGE this value */
199         int         silent = 0;
200         int         fd;
201         char        data;
202         int         c;
203         int         i;
204         extern char *optarg;
205
206         set_progname(argv[0]);
207
208         setlocale(LC_ALL, "");
209         bindtextdomain(PACKAGE, LOCALEDIR);
210         textdomain(PACKAGE);
211
212         if (argc == 2 &&
213             (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")))
214                 print_version_and_exit();
215
216         while ( (c = getopt( argc, argv, "r:d:sv" )) != EOF ) {
217                 switch (c) {
218                 case 'r':
219                         rate = atof( optarg );
220                         break;
221                 case 'd':
222                         delay = atoi( optarg );
223                         break;
224                 case 's':
225                         silent = 1;
226                         break;
227                 default:
228                         fprintf(stderr,
229                                 _("Usage: kbdrate [-V] [-s] [-r rate] [-d delay]\n"));
230                         exit(1);
231                 }
232         }
233
234         if(KDKBDREP_ioctl_ok(rate, delay, silent))      /* m68k? */
235                 return 0;
236
237         if(KIOCSRATE_ioctl_ok(rate, delay, silent))     /* sparc? */
238                 return 0;
239
240
241         /* The ioport way */
242
243         for (i = 0; i < RATE_COUNT; i++)
244                 if (rate * 10 >= valid_rates[i]) {
245                         value &= 0x60;
246                         value |= i;
247                         break;
248                 }
249
250
251         for (i = 0; i < DELAY_COUNT; i++)
252                 if (delay <= valid_delays[i]) {
253                         value &= 0x1f;
254                         value |= i << 5;
255                         break;
256                 }
257
258         if ( (fd = open( "/dev/port", O_RDWR )) < 0) {
259                 perror( _("Cannot open /dev/port") );
260                 exit( 1 );
261         }
262
263         do {
264                 lseek( fd, 0x64, 0 );
265                 read( fd, &data, 1 );
266         } while ((data & 2) == 2 );  /* wait */
267
268         lseek( fd, 0x60, 0 );
269         data = 0xf3;                 /* set typematic rate */
270         write( fd, &data, 1 );
271
272         do {
273                 lseek( fd, 0x64, 0 );
274                 read( fd, &data, 1 );
275         } while ((data & 2) == 2 );  /* wait */
276
277         lseek( fd, 0x60, 0 );
278         sleep( 1 );
279         write( fd, &value, 1 );
280
281         close( fd );
282
283         if (!silent)
284                 printf( _("Typematic Rate set to %.1f cps (delay = %d ms)\n"),
285                         valid_rates[value & 0x1f] / 10.0,
286                         valid_delays[ (value & 0x60) >> 5 ] );
287
288         return 0;
289 }