linux linux-kernel driver linux-device-driver ioctl

Controlador de dispositivo IOCTL Linux



linux-kernel driver (2)

Un ioctl , que significa "control de entrada-salida" es un tipo de llamada al sistema específica del dispositivo. Hay solo unas pocas llamadas al sistema en Linux (300-400), que no son suficientes para expresar todas las funciones únicas que los dispositivos pueden tener. Por lo tanto, un controlador puede definir un ioctl que permite a una aplicación de espacio de usuario enviar sus pedidos. Sin embargo, ioctls no son muy flexibles y tienden a estar un poco desordenados (docenas de "números mágicos" que simplemente funcionan ... o no), y también pueden ser inseguros, al pasar un buffer al kernel - el mal manejo puede romperse cosas fácilmente

Una alternativa es la interfaz sysfs , donde configura un archivo en /sys/ y lee / escribe para obtener información del controlador. Un ejemplo de cómo configurar esto:

static ssize_t mydrvr_version_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s/n", DRIVER_RELEASE); } static DEVICE_ATTR(version, S_IRUGO, mydrvr_version_show, NULL);

Y durante la instalación del controlador:

device_create_file(dev, &dev_attr_version);

A continuación, tendrá un archivo para su dispositivo en /sys/ , por ejemplo, /sys/block/myblk/version para un controlador de bloque.

Otro método para un uso más pesado es netlink, que es un método de IPC (comunicación entre procesos) para hablar con su controlador a través de una interfaz de socket BSD. Esto se usa, por ejemplo, con los controladores WiFi. Luego se comunica con él desde el espacio de usuario utilizando las bibliotecas libnl o libnl3 .

¿Alguien puede explicarme,

  1. ¿Qué es IOCTL ?
  2. ¿Para qué se usa esto?
  3. ¿Como puedo usar lo?
  4. ¿Por qué no puedo definir una nueva función que haga el mismo trabajo que IOCTL ?

ioctl función ioctl es útil cuando uno está implementando un controlador de dispositivo para establecer la configuración en el dispositivo. por ejemplo, una impresora tiene opciones de configuración para verificar y configurar la fuente, el tamaño de la fuente, etc. ioctl podría usar para obtener la fuente actual y establecer la fuente a otra. En la aplicación de usuario, use ioctl para enviar un código a una impresora diciéndole que devuelva la fuente actual o que establezca la fuente en una nueva.

int ioctl(int fd, int request, ...)

  1. fd es el descriptor de archivo, el que se devuelve abierto
  2. request es código de solicitud. por ejemplo, GETFONT obtendrá la fuente actual de la impresora, SETFONT configurará la fuente en una impresora.
  3. el tercer argumento es void * . Dependiendo del segundo argumento, el tercero puede o no estar presente. por ejemplo, si el segundo argumento es SETFONT, el tercer argumento puede dar el nombre de la fuente como ARIAL.

Entonces, ahora la solicitud int no es solo una macro, se requiere una para generar el código de solicitud que utilizará la aplicación del usuario y el módulo del controlador del dispositivo para determinar con qué configuración debe jugarse el dispositivo. Uno envía un código de solicitud usando ioctl desde la aplicación del usuario y luego usa el código de solicitud en el módulo del controlador del dispositivo para determinar qué acción realizar.

Un código de solicitud tiene 4 partes principales

1. A Magic number - 8 bits 2. A sequence number - 8 bits 3. Argument type (typically 14 bits), if any. 4. Direction of data transfer (2 bits).

Si el código de solicitud es SETFONT para configurar la fuente en una impresora, la dirección para la transferencia de datos será desde la aplicación del usuario hasta el módulo del controlador del dispositivo. El usuario envía el nombre de la fuente Arial a la impresora. Si el código de solicitud es GETFONT, la dirección es de la impresora a la aplicación del usuario.

Para generar el código de solicitud, Linux proporciona alguna función predefinida, como macros.

1. _IO(MAGIC, SEQ_NO) ambos son 8 bits, 0 a 255, por ejemplo, digamos que queremos pausar la impresora. Esto no requiere una transferencia adata. Entonces, generaríamos el código de solicitud de la siguiente manera

#define PRIN_MAGIC ''P'' #define NUM 0 #define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)

Ahora usa ioctl como

ret_val = ioctl(fd, PAUSE_PRIN);

La llamada al sistema correspondiente en el módulo del controlador recibirá el código y pausará la impresora.

  1. __IOW(MAGIC, SEQ_NO, TYPE) MAGIC y SEQ_NO son los mismos que los de arriba, pero la tercera parte proporciona el tipo de siguiente argumento, recuerda que el tercer argumento de ioctl es void * . W en __IOW indica que la dirección de los datos es desde la aplicación del usuario al módulo del controlador. Tomemos un ejemplo, supongamos que uno le está diciendo a la impresora que configure la fuente en Arial.

    #define PRIN_MAGIC ''S'' #define SEQ_NO 1 #define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)

promover,

char *font = "Arial"; ret_val = ioctl(fd, SETFONT, font);

Ahora la font es un puntero, lo que significa que es una dirección mejor representada como unsigned long , por lo tanto, la tercera parte de _IOW menciona el tipo como tal. Además, esta dirección de fuente se pasa a la llamada al sistema correspondiente implementada en el módulo del controlador del dispositivo como unsigned long y tenemos que convertirla en el tipo apropiado antes de usarla. El espacio del núcleo puede acceder al espacio de usuario y, por lo tanto, esto funciona. otras dos funciones como macros son __IOR(MAGIC, SEQ_NO, TYPE) y __IORW(MAGIC, SEQ_NO, TYPE) donde la dirección del flujo de datos será desde el espacio del núcleo hasta el espacio del usuario y en ambos sentidos, respectivamente.

Por favor, avíseme si esto ayuda!