Playing with the keyboard LEDs under Linux

To make your programs more attractive to the eye and show off with your skills, you can make the keyboard LEDs, showing the state of the Num Lock, Caps Lock, Scroll Lock keys, flash in some way.
Now I'll show you how to do it.

To manipulate a character device (the keyboard) use the sys_ioctl system call (number 54). First you need to know what commands to use. To get those, read the /usr/include/linux/kd.h file or the manual, if you have the ioctl_list page. There we have:


(skip the commands)
	#define KDGETLED 0x4B31  /* get current LED status */
	#define KDSETLED 0x4B32  /* set new LED status */
	#define LED_SCR  0x01    /* scroll lock */
	#define LED_NUM  0x02    /* num lock */
	#define LED_CAP  0x04    /* caps lock */

One of the first two values will be put into ECX.
Meanwhile, EBX should contain a descriptor of /dev/console open for writing or the value 1, meaning the standard output device - STDOUT.
EDX will contain the last parameter: a value in the range 0-7, if we want to set the LED state, or an address of a DWORD variable, which will receive the current LED state (also in the range 0-7).

Now we can see what our program should do: save the current LED state, change them as you like (and make it last - do pauses), bring back the old state.
Here's how such a program could look like (using my library isn't necessary - the comments tell what to change.


(skip example code)
; Program manipulates the keyboard LEDs
;
; Author: Bogdan D.
; Contact: bogdandr (at) op (dot) pl
;
; nasm -f elf klaw.asm
; ld -s -o klaw klaw.o


section .text

		; you don't have to use these:
%include "bibl/incl/linuxbsd/nasm/n_system.inc"
%include "bibl/incl/linuxbsd/nasm/n_const.inc"

%define	KDGETLED	0x4b31
%define	KDSETLED	0x4b32

global _start

_start:

; 1. open /dev/console, in write-only mode or use STDOUT

	mov	eax, sys_open	; sys_open = 5 (open the file)
	mov	ebx, konsola	; address of the file name
	mov	ecx, O_WRONLY	; O_WRONLY = 01
	mov	edx, 777q	; RWX for all
	int	80h

	cmp	eax, 0
	jge	.ok		; continue if no error

				; if error, use STDOUT
	mov	eax, stdout	; stdout = 1

; 2. get current LED state

.ok:
	mov	ebx, eax	; EBX = file descriptor

	mov	eax, sys_ioctl	; sys_ioctl = 54 - device manipulation
	mov	ecx, KDGETLED	; get led state
	mov	edx, stare_diody	; address of a DWORDa for the
					; current LED state
	int	80h

	mov	eax, sys_ioctl	; sys_ioctl = 54
	mov	ecx, KDSETLED	; set LED state
	mov	edx, 7		; all set to "on"
	int	80h

        mov	cx, 7
        mov	dx, 0a120h	; delay for half a second
	call	pauza

; bring back the old state

	mov	eax, sys_ioctl
	mov	ecx, KDSETLED		; set LED state
	mov	edx, [stare_diody]	; EDX = previous LED state
	int	80h

	cmp	ebx, stdout	; did we open the console or STDOUT?
	jle	.koniec		; don't close the STDOUT

	mov	eax, sys_close	; close the open console file
	int	80h

.koniec:
	wyjscie			;
				;	mov	eax, 1
				;	xor	ebx, ebx
				;	int	80h


pauza:			; procedure pausing for CX:DX miliseconds
	push    ebx
	push    ecx
	push    edx

	mov     ax, cx
	shl     eax, 16
	mov     ebx, 1000000
	mov     ax, dx 		; EAX = CX:DX
	xor     edx, edx
	div     ebx		; divide CX:DX by 1 million
	mov     [t1 + timespec.tv_sec], eax 	; EAX = number of seconds

	mov     ebx, 1000
	mov     eax, edx	; EAX = number of microseconds left
	mul     ebx
	mov     [t1 + timespec.tv_nsec], eax 	; EAX = number of nanoseconds

	mov     eax, sys_nanosleep	; function number 162
	mov     ebx, t1
	mov     ecx, t2
	int     80h

	pop     edx
	pop     ecx
	pop     ebx

	ret

section .data

stare_diody	dd	0
konsola		db	"/dev/console",0

; The timespec structure is defined in n_system.inc (from my library)
;struc timespec
;	        .tv_sec:		resd 1
;	        .tv_nsec:		resd 1
;endstruc

t1 istruc timespec
t2 istruc timespec

Further experiments are left as an excercise for the user. Remember there are 8 different possible combinations for the LED state and you can make the different durations between state changes.

Have fun.


On-line contents (Alt+2)
Helpers for people with disabilities (Alt+0)