summaryrefslogtreecommitdiffstats
path: root/kbd/kbdrate.c
blob: df116e1e074f5c3e7c25fe053964f4f5ba203216 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
From: faith@cs.unc.edu (Rik Faith)
Subject: User mode keyboard rate changer
Date: 27 Apr 92 13:44:26 GMT

I put together this program, called kbdrate.c, which will reset the keyboard
repeat rate and delay in user mode.  The program must have read/write
access to /dev/port, so if /dev/port is only read/writeable by group port,
then kbdrate must run setgid to group port (for example).

The "rate" is the rate in characters per second

The "delay" is the amount of time the key must remain depressed before it
will start to repeat.

Usage examples:

kbdrate                 set rate to IBM default (10.9 cps, 250ms delay)
kbdrate -r 30.0         set rate to 30 cps and delay to 250ms
kbdrate -r 20.0 -s      set rate to 20 cps (delay 250ms) -- don't print message
kbdrate -r 0 -d 0       set rate to 2.0 cps and delay to 250 ms

I find it useful to put kbdrate in my /etc/rc file so that the keyboard
rate is set to something that I find comfortable at boot time.  This sure
beats rebuilding the kernel!


  kbdrate.c -- Set keyboard typematic rate (and delay)
  Created: Thu Apr 23 12:24:30 1992
  Author: Rickard E. Faith, faith@cs.unc.edu

  Copyright 1992 Rickard E. Faith.  Distributed under the GPL.
  This program comes with ABSOLUTELY NO WARRANTY.
  Usage: kbdrate [-r rate] [-d delay] [-s]
         Rate can range from 2.0 to 30.0 (units are characters per second)
         Delay can range from 250 to 1000 (units are milliseconds)
         -s suppressed message
  Compiles under gcc 2.1 for Linux (tested with the pre-0.96 kernel)
 
  Wed Jun 22 21:35:43 1994, faith@cs.unc.edu:
            Changed valid_rates per suggestion by Andries.Brouwer@cwi.nl.
  Wed Jun 22 22:18:29 1994, faith@cs.unc.edu:
            Added patch for AUSTIN notebooks from John Bowman
            (bowman@hagar.ph.utexas.edu)
 
  Linux/68k modifications by Roman Hodek 
 				(Roman.Hodek@informatik.uni-erlangen.de):
 
  Reading/writing the Intel I/O ports via /dev/port is not the
  English way... Such hardware dependant stuff can never work on
  other architectures.
  
  Linux/68k has an new ioctl for setting the keyboard repeat rate
  and delay. Both values are counted in msecs, the kernel will do
  any rounding to values possible with the underlying hardware.
 
  kbdrate now first tries if the KDKBDREP ioctl is available. If it
  is, it is used, else the old method is applied.

  1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
  - added Native Language Support

  1999-03-17
  Linux/SPARC modifications by Jeffrey Connell <ankh@canuck.gen.nz>:
  It seems that the KDKBDREP ioctl is not available on this platform.
  However, Linux/SPARC has its own ioctl for this, with yet another
  different measurement system.  Thus, try for KIOCSRATE, too.

*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/ioctl.h>

#include "../defines.h"
#ifdef HAVE_kd_h
#include <linux/kd.h>
#endif
#ifdef __sparc__
#include <asm/param.h>
#include <asm/kbio.h>
#endif

#include "nls.h"

static int valid_rates[] = { 300, 267, 240, 218, 200, 185, 171, 160, 150,
                                   133, 120, 109, 100, 92, 86, 80, 75, 67,
                                   60, 55, 50, 46, 43, 40, 37, 33, 30, 27,
                                   25, 23, 21, 20 };
#define RATE_COUNT (sizeof( valid_rates ) / sizeof( int ))

static int valid_delays[] = { 250, 500, 750, 1000 };
#define DELAY_COUNT (sizeof( valid_delays ) / sizeof( int ))

static int
KDKBDREP_ioctl_ok(double rate, int delay, int silent) {
#ifdef KDKBDREP
     /* This ioctl is defined in <linux/kd.h> but is not
	implemented anywhere - must be in some m68k patches. */
   struct kbd_repeat kbdrep_s;

   /* don't change, just test */
   kbdrep_s.rate = -1;
   kbdrep_s.delay = -1;
   if (ioctl( 0, KDKBDREP, &kbdrep_s )) {
     if (errno == EINVAL)
       return 0;
     perror( "ioctl(KDKBDREP)" );
     exit( 1 );
   }

   /* do the change */
   if (rate == 0)				/* switch repeat off */
     kbdrep_s.rate = 0;
   else
     kbdrep_s.rate  = 1000.0 / rate;		/* convert cps to msec */
   if (kbdrep_s.rate < 1)
     kbdrep_s.rate = 1;
   kbdrep_s.delay = delay;
   if (kbdrep_s.delay < 1)
     kbdrep_s.delay = 1;
   
   if (ioctl( 0, KDKBDREP, &kbdrep_s )) {
     perror( "ioctl(KDKBDREP)" );
     exit( 1 );
   }

   /* report */
   if (kbdrep_s.rate == 0)
     rate = 0;
   else
     rate = 1000.0 / (double) kbdrep_s.rate;

   if (!silent)
     printf( _("Typematic Rate set to %.1f cps (delay = %d ms)\n"),
	     rate, kbdrep_s.delay );

   return 1;			/* success! */

#else /* no KDKBDREP */
   return 0;
#endif /* KDKBDREP */
}

static int
KIOCSRATE_ioctl_ok(double rate, int delay, int silent) {
#ifdef KIOCSRATE
   struct kbd_rate kbdrate_s;
   int fd;

   fd = open("/dev/kbd", O_RDONLY);
   if (fd == -1) {
     perror( "open(/dev/kbd)" );
     exit( 1 );
   }

   kbdrate_s.rate = (int) (rate + 0.5);  /* must be integer, so round up */
   kbdrate_s.delay = delay * HZ / 1000;  /* convert ms to Hz */
   if (kbdrate_s.rate > 50)
     kbdrate_s.rate = 50;

   if (ioctl( fd, KIOCSRATE, &kbdrate_s )) {
     perror( "ioctl(KIOCSRATE)" );
     exit( 1 );
   }
   close( fd );

   if (!silent)
     printf( "Typematic Rate set to %d cps (delay = %d ms)\n",
	     kbdrate_s.rate, kbdrate_s.delay * 1000 / HZ );

   return 1;
#else /* no KIOCSRATE */
   return 0;
#endif /* KIOCSRATE */
}

int main( int argc, char **argv )
{
#ifdef __sparc__
   double      rate = 5.0;      /* Default rate */
   int         delay = 200;     /* Default delay */
#else
   double      rate = 10.9;     /* Default rate */
   int         delay = 250;     /* Default delay */
#endif
   int         value = 0x7f;    /* Maximum delay with slowest rate */
                                /* DO NOT CHANGE this value */
   int         silent = 0;
   int         fd;
   char        data;
   int         c;
   int         i;
   extern char *optarg;

   setlocale(LC_ALL, "");
   bindtextdomain(PACKAGE, LOCALEDIR);
   textdomain(PACKAGE);
   

   while ( (c = getopt( argc, argv, "r:d:svVh?" )) != EOF ) {
         switch (c) {
         case 'r':
            rate = atof( optarg );
            break;
         case 'd':
            delay = atoi( optarg );
            break;
         case 's':
            silent = 1;
            break;
	 case 'v':
	 case 'V':
	    fprintf( stderr, "util-linux %s kbdrate\n", UTIL_LINUX_VERSION);
	    exit(0);
	 case 'h':
	 case '?':
	    fprintf( stderr,
		  _("Usage: kbdrate [-V] [-s] [-r rate] [-d delay]\n"));
	    exit(0);
         }
   }

   if(KDKBDREP_ioctl_ok(rate, delay, silent)) 	/* m68k? */
	return 0;

   if(KIOCSRATE_ioctl_ok(rate, delay, silent))	/* sparc? */
	return 0;


    /* The ioport way */

    for (i = 0; i < RATE_COUNT; i++)
      if (rate * 10 >= valid_rates[i]) {
	value &= 0x60;
	value |= i;
	break;
      }


      for (i = 0; i < DELAY_COUNT; i++)
	if (delay <= valid_delays[i]) {
	  value &= 0x1f;
	  value |= i << 5;
	  break;
	}

      if ( (fd = open( "/dev/port", O_RDWR )) < 0) {
	perror( _("Cannot open /dev/port") );
	exit( 1 );
      }

      do {
	lseek( fd, 0x64, 0 );
	read( fd, &data, 1 );
      } while ((data & 2) == 2 );  /* wait */

      lseek( fd, 0x60, 0 );
      data = 0xf3;                 /* set typematic rate */
      write( fd, &data, 1 );

      do {
	lseek( fd, 0x64, 0 );
	read( fd, &data, 1 );
      } while ((data & 2) == 2 );  /* wait */

      lseek( fd, 0x60, 0 );
      sleep( 1 );
      write( fd, &value, 1 );

      close( fd );

      if (!silent)
	      printf( _("Typematic Rate set to %.1f cps (delay = %d ms)\n"),
                           valid_rates[value & 0x1f] / 10.0,
                           valid_delays[ (value & 0x60) >> 5 ] );

   return 0;
}