
   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/nasm/n_system.inc"
%include "bibl/incl/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)
