ioctl
ioctl es una llamada de sistema en Unix que permite a una aplicación controlar o comunicarse con un driver de dispositivo, fuera de los usuales read/write de datos. Esta llamada se originó en la versión 7 del AT&T Unix. Su nombre abrevia la frase input/output control.
Descripción
Una llamada ioctl toma como parámetros:
- un descriptor de archivos abierto
- un número de código de requerimiento
- también un valor entero, posiblemente sin signo (va al driver), o un puntero a datos (también va al driver y vuelve de él)
El kernel generalmente envía un ioctl directamente al driver, el cual puede interpretar el número de requerimiento y datos en cualquier forma requerida. Los escritores del driver documentan cada número de requerimiento del driver para ese driver particular, y los proveen de constantes en el archivo de cabeceras (*.h)
Algunos sistemas tienen convenciones en su codificación, dentro del número de codificación, el tamaño de los datos a ser transferidos desde o hacia el driver del dispositivo, la dirección de la transferencia de datos y la identidad del driver implementando el requerimiento.
Independientemente de que esa convención se cumpla o no, el kernel y el driver colaboran para entregar un código de error uniforme (señalados por la constante simbólica ENOTTY) a una aplicación que haga un requerimiento al driver que no reconozca.
El nemónico ENOTTY (tradicionalmente asociado con el mensaje textual "Not a typerwriter") viene del hecho de que en los sistemas iniciales que incorporaba una llamada ioctl, solo el dispositivo teletipo (tty) planteaba este error. A través del nemónico simbólico es ajustado por los requerimientos de compatibilidad; algunos sistemas modernos muy útilmente prestan un mensaje más general, como: "Inappropriate device control operation", o la localización del mismo.
TCSETS ejemplifica un ioctl en un puerto serial. Las llamadas de lectura y escritura normales en un puerto serial reciben y envían paquetes de datos. Una llamada ioctl(fd, TCSETS, data), independiente de las llamadas normales I/O, controla varias opciones del controlador, como la manipulación de caracteres especiales, o las señales de salida en el puerto (por ejemplo, la señal DTR).
Ejemplo de uso
Un ejemplo de uso para ioctl es el manejo de los leds del teclado, KDGKLED, KDSKBLED son las macros definidas para poder acceder a los leds que la mayoría de los teclados tienen.
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <time.h>
#include <unistd.h> /*Encabezado para la función sleep() and close() */
int get_state(int fd,int *estado)
{
if (-1 == ioctl(fd, KDGKBLED, estado)){
/*Si la función ioctl devuelve -1, hubo un error, que será especificado en la función perror(), si es cero significa que finalizo exitosamente*/
perror("ioctl");
close(fd);
return -1;
}
return 0;
}
void print_caps_lock_state(int estado)
{
printf("Estado Caps Lock: %s (%d)\n",
(estado & K_CAPSLOCK) == K_CAPSLOCK ? "on" : "off", estado);
}
int apaga(int fd,int *estado)
{
get_state(fd,estado);
if (-1 == ioctl(fd, KDSKBLED, *estado ^ K_CAPSLOCK)){
perror("ioctl set");
close(fd);
return -1;
}
return 0;
}
int prende(int fd,int *estado)
{
if (-1 == ioctl(fd, KDSKBLED, K_CAPSLOCK)){
perror("ioctl set");
close(fd);
return -1;
}
get_state(fd,estado);
return 0;
}
int main()
{
int fd = open("/dev/tty0", O_NOCTTY);
if (fd == -1){
perror("open");
return -1;
}
int estado = 0;
prende(fd,&estado);
sleep(1);
apaga(fd,&estado);
sleep(2);
prende(fd,&estado);
sleep(1);
apaga(fd,&estado);
close(fd);
return 0;
}