Using ioctl() - THE.UNIX.WORLD
Pubblicato da mammon_ il 11/1999
Livello base

Introduzione

/* Questo articolo e' stato tratto dall'Assembly Programming Journal numero 6 */
::/ \::::::.
:/___\:::::::.
/|    \::::::::.
:|   _/\:::::::::.
:| _ |\ \::::::::::.
:::\_____\:::::::::::............................................THE.UNIX.WORLD

Iniziamo

One of the most famous Unix maxims reads 'everything is a file'; directories are files, pipes are files, hardware devices are files, even files are files. This provided a transparent means or reading and writing hardware or software constructs such as modems and sockets; yet the lack of interrupts or device driver routines is sometimes confusing for those not used to Unix programming. In linux, handling device parameters through the character and block 'special file' interface is handled through ioctl().
The ioctl() system call takes a file descriptor and a request type as its primary arguments, along with an optional third argument referred to as "argp" which contains any arguments that must be passed along with the request. The possible ioctl() requests can be found by poking around in the $INCLUDE/asm and $INCLUDE/linux header files, although a somewhat dated list of requests can be viewed by typing 'man ioctl_list'.
One of the most useful devices to program with ioctl() for the applications programmer will be the console; in linux terms, this consists of the keyboard and display, such that all 63 of the Virtual Consoles can be controlled with ioctl(). This can be useful if one wants to output debugging information to a non-visible console, or to transfer STDIN and STDOUT to a newly-allocated console while disabling virtual console switching, effectively tying the user to a single console [e.g., in a walkup workstation].
Information on console ioctl requests can be found with 'man console_ioctl'. Bringing up this man page instantly displays the following text:

WARNING: If you use the following information you are going to burn yourself.
WARNING: ioctl's are undocumented Linux internals, liable to be changed without warning. Use POSIX functions. This is ancient asm coderspeak meaning 'you are on the right track, keep going.'

Perusing the listed requests will provide enough information to code that first exercise from DOS-ASM 1o1: generating a tone on the PC speaker.
KDMKTONE Generate tone of specified length. The lower 16 bits of argp specify the period in clock cycles, and the upper 16 bits give the duration in msec.
If the duration is zero, the sound is turned off. Control returns immediately. For example, argp = (125<<16) + 0x637 would specify the beep normally associated with a ctrl-G. (Thus since 0.99pl1; broken in 2.1.49-50.)
This should not be too terribly hard to implement -- a call to open the file descriptor, and a single call to ioctl() to sound the tone. First things first, open() is called on /dev/tty to create a handle for the current console:


#-------------------------------------------------------------------beep.asm
%define O_RDWR 2 ;grep O_RDWR /usr/include/asm/*
%define KDMKTONE 0x4B30 ;grep KDMKTONE /usr/include/linux/*
EXTERN open
GLOBAL main

section .data
szTTY db '/dev/tty',0

section .text
main:
push dword O_RDWR
push dword szTTY
call open
add esp, 8
#--------------------------------------------------------------------BREAK
Next, calculate the frequency and duration of the tone to be played:

#---------------------------------------------------------------------CONT
mov dx, 666 ;duration
shl edx, 16
or dx, 1199 ;tone
#--------------------------------------------------------------------BREAK
Now, normally one might call ioctl as so:

push edx
push dword KDMKTONE
push eax
call ioctl
add esp, 12
However, ioctl is a systemcall, and we can save a bit of time by going straight through the syscall gate at 0x80:

#---------------------------------------------------------------------CONT
mov ebx, eax
mov ecx, KDMKTONE
mov eax, 54 ;ioctl func defined in /usr/include/asm/unistd.h
int 0x80
ret
#----------------------------------------------------------------------EOF
So much for the simple beep. Another ASM 101 favorite is the 'blinking LED' trick, where students learn to make the keyboard LEDs blink on and off in any number of psychedelic patterns. A quick tour through the man page shows the requests needed for this sample as well:
KDGETLED
Get state of LEDs. argp points to a long int. The lower three bits of *argp are set to the state of the LEDs, as follows:

LED_CAP 0x04 caps lock led
LED_NUM 0x02 num lock led
LED_SCR 0x01 scroll lock led
KDSETLED
Set the LEDs. The LEDs are set to correspond to the lower three bits of argp. However, if a higher order bit is set, the LEDs revert to normal: displaying the state of the keyboard functions of caps lock, num lock, and scroll lock.
The file descriptor must be opened as with the previous example. From there, we must get the current LED state:

#--------------------------------------------------------------------led.asm
%define KDGETLED 0x4B31 ;grep KDGETLED /usr/include/linux/*
%define KDSETLED 0x4B32 ;grep KDSETLED /usr/include/linux/*

xor edx, edx
mov ecx, KDGETLED
mov ebx, eax
mov eax, 54
int 0x80
#--------------------------------------------------------------------BREAK
Next, all of the LEDs will be turned on and then off 10 times. It is vital to the success of the algorithm that a delay be present between the off and on transitions; otherwise the LEDs will appear to be steadily lit, and that is much less of a programming achievement:

#---------------------------------------------------------------------CONT
mov ecx, 10
.here:
push ecx ;save counter
or edx, 0x07 ;set all of 'em
mov ecx, KDSETLED
mov eax, 54
int 0x80

mov ecx, 0xFFFFFF ;delay counter
.delay:
loop .delay

and edx, 0 ;turn all of them off
mov ecx, KDSETLED
mov eax, 54
int 0x80

mov ecx, 0xFFFFFF ;next delay counter
.delay2:
loop .delay2

pop ecx
loop .here

ret
#----------------------------------------------------------------------EOF
Blinking the LEDs in succession and achieving hypnotic frequency via ioctl() will be left as an exercise to the reader.
This should provide a quick introduction to using ioctl(). There are many more possibilities available for scan codes, screen painting, and virtual console control; further opportunities for console amusement exist also within the realm of escape-sequence programming. The examples presented here can be compiled with the standard nasm -f elf file.asm gcc -o file file.o combination, or by using a Makefile:

#----------------------------------------------------------------------Makefile
TARGET =beep #TARGET is the variable storing the base filename

ASM = nasm #ASM contains the name of the assembler
ASMFILE = $(TARGET).asm #ASMFILE contains the full name of the source file
OBJFILE = $(TARGET).o #OBJFILE contains the full name of the object file
LINKER = gcc #LINKER contains the full name of the linker
LIBS = #LIBS contains any library flags
LIBDIR = #LIBDIR contains any library location flags

all: #the 'all:' section applies to all targets
$(ASM) -o $(OBJFILE) -f elf $(ASMFILE)
$(LINKER) -o $(TARGET) $(OBJFILE) $(LIBDIR) $(LIBS)
#---------------------------------------------------------------------------EOF
As with all Makefiles, with the target correctly set the source will be compiled and linked simply by typing 'make' in the directory where the Makefile is located.