DeviceDriver (7): bloqueo y no bloqueo

Uno: bloqueo y no bloqueo

1. Cuando la aplicación está funcionando en el controlador del dispositivo, si el recurso del dispositivo no se puede obtener inmediatamente.

Para bloquear IO, el hilo correspondiente se suspenderá hasta que se obtenga el recurso del dispositivo.

Para E / S sin bloqueo, el hilo no se suspenderá, pero siempre sondeará y esperará hasta que los recursos del dispositivo estén disponibles, o simplemente se rendirá.

2. Implementación del programa de aplicación

Bloqueo:

fd = open("/dev/xxx_dev", O_RDWR); /* 阻塞方式打开 */

Sin bloqueo:

fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); /* 非阻塞方式打开 */

Dos: cola de espera (bloqueo)

        Acceda al dispositivo en modo de bloqueo. Cuando los recursos del dispositivo no están disponibles, el proceso entrará en estado de suspensión y liberará los recursos de la CPU. El proceso debe activarse cuando el archivo del dispositivo está operativo, generalmente en un programa de interrupción. El kernel de Linux proporciona una cola de espera para realizar la activación de los procesos bloqueados.

1. Espere la creación e inicialización del encabezado de la cola.

struct __wait_queue_head {
	spinlock_t		lock;
	struct list_head	task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

wait_queue_head_t	read_wait;
init_waitqueue_head(&dev->read_wait);

O utilice una definición de macro para completar la definición y la inicialización del encabezado de la cola de espera de una vez:

#define DECLARE_WAIT_QUEUE_HEAD(name) \
	wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

2. Esperando elementos en cola

struct __wait_queue {
	unsigned int		flags;
	void			*private;
	wait_queue_func_t	func;
	struct list_head	task_list;
};

typedef struct __wait_queue wait_queue_t;

wait_queue_t		wait;

Definición de macro: nombre es el nombre del elemento de la cola de espera, tsk es a qué tarea (proceso) pertenece el elemento de la cola de espera, generalmente configurado como actual, actual es equivalente a una variable global en el kernel de Linux, que representa el proceso actual.

#define DECLARE_WAITQUEUE(name, tsk)					\
	wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

3. Agregar o quitar elementos del encabezado de la cola

        Cuando el dispositivo es inaccesible, el elemento de la cola de espera correspondiente al proceso debe agregarse a la cabecera de la cola de espera creada anteriormente, y el proceso puede entrar en el estado de suspensión solo después de agregarse a la cabecera de la cola de espera. Cuando el dispositivo sea accesible, elimine la cola de espera correspondiente al proceso de la cola de espera.

Espere a que el elemento de la cola agregue API:

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

Esperando a que se elimine el elemento de la cola API:

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

4. Espere a que se despierte

Cuando se puede usar el dispositivo, es necesario reactivar el proceso que entró en suspensión.

void wake_up(wait_queue_head_t *q)
void wake_up_interruptible(wait_queue_head_t *q)

El parámetro q es el encabezado de la cola de espera para ser despertado Wake_up puede despertar procesos en los estados TASK_INTERRUPTIBLE y TASK_UNINTERRUPTIBLE, mientras que wake_up_interruptible solo puede despertar procesos en el estado TASK_INTERRUPTIBLE.

5. Esperando el evento

Además de despertar activamente, también puede configurar la cola de espera para que espere un evento, y cuando el evento esté satisfecho, el proceso en la cola de espera se despertará automáticamente.

(1) wait_event: Esperando que se despierte la cola de espera con wq como cabeza de la cola de espera, siempre que se deba cumplir la condición para que sea verdadera, de lo contrario se ha bloqueado. Esta función establecerá el proceso en el estado TASK_UNINTERRUPTIBLE.

#define wait_event(wq, condition)					\
do {									\
	might_sleep();							\
	if (condition)							\
		break;							\
	__wait_event(wq, condition);					\
} while (0)

(2) wait_event_timeout: la función es similar a wait_event, pero esta función puede agregar un período de tiempo de espera en jiffies. Esta función tiene un valor de retorno. Devolver 0 significa que el período de tiempo de espera ha terminado y la condición es falsa. Si es 1, la condición es verdadera, es decir, se cumple la condición.

#define wait_event_timeout(wq, condition, timeout)			\
({									\
	long __ret = timeout;						\
	might_sleep();							\
	if (!___wait_cond_timeout(condition))				\
		__ret = __wait_event_timeout(wq, condition, timeout);	\
	__ret;								\
})

(3) wait_event_interruptible: similar a la función wait_event, pero esta función establece el proceso en TASK_INTERRUPTIBLE, lo que significa que puede ser interrumpido por una señal.

#define wait_event_interruptible(wq, condition)				\
({									\
	int __ret = 0;							\
	might_sleep();							\
	if (!(condition))						\
		__ret = __wait_event_interruptible(wq, condition);	\
	__ret;								\
})

(4) wait_event_interruptible_timeout: similar a la función wait_event_timeout, esta función también establece el proceso en TASK_INTERRUPTIBLE, que puede ser
interrumpido por una señal .

Tres: sondeo (sin bloqueo)

        Si la aplicación de usuario accede al dispositivo de forma no bloqueante, el controlador del dispositivo debe proporcionar un método de procesamiento sin bloqueo, es decir, sondeo. Poll, epoll y select se pueden utilizar para procesar el sondeo, y la aplicación puede consultar si el dispositivo es operable a través de la función select, epoll o poll.

1. La función de selección

int select(int nfds, 
        fd_set *readfds, 
        fd_set *writefds,
        fd_set *exceptfds, 
        struct timeval *timeout)

(1) nfds: el número de descriptores de archivo que se utilizarán.

(2) readfds, writefds, exceptfds: tres punteros apuntan al conjunto de descriptores, cada bit de la variable de tipo fd_set representa un descriptor de archivo. readfds se usa para monitorear los cambios de lectura del conjunto de descriptores especificado y si se puede leer. Si los hay, devuelve un valor mayor que 0 para indicar que el archivo se puede leer. Si no hay ningún archivo para leer, juzgar si se ha agotado el tiempo de espera según el parámetro de tiempo de espera. Puede establecer readfds en NULL, lo que significa que no le importan los cambios de lectura de archivos. writefds es similar, usado para monitorear si estos archivos se pueden escribir. exceptfds se utiliza para controlar las anomalías de estos archivos.

API de operación de la variable Fd_set: FD_ZERO se usa para borrar todos los bits de la variable fd_set, FD_SET se usa para establecer una posición determinada, es decir, para agregar un descriptor de archivo a fd_set, y el parámetro fd es el descriptor de archivo que se agregará. FD_CLR elimina un descriptor de archivo de fd_set. FD_ISSET se usa para probar si un cierto bit de fd_set está establecido en 1, es decir, para determinar si se puede operar un determinado archivo.

void FD_ZERO(fd_set *set)
void FD_SET(int fd, fd_set *set)
void FD_CLR(int fd, fd_set *set)
int FD_ISSET(int fd, fd_set *set)

(3) tiempo de espera: tiempo de espera, cuando es NULL, significa esperar indefinidamente.

struct timeval
{
  __time_t tv_sec;		/* Seconds.  */
  __suseconds_t tv_usec;	/* Microseconds.  */
};

(4) Valor de retorno: 0 significa que se agotó el tiempo de espera, pero no se puede operar ningún descriptor de archivo; -1 significa que ha ocurrido un error; otros valores indican el número de descriptores de archivo que se pueden operar.

2, función de encuesta

       En un solo hilo, la cantidad de descriptores de archivo que la función de selección puede monitorear tiene un límite máximo, generalmente 1024. La función de encuesta es similar a la función de selección, pero no hay limitación en el número máximo de descriptores.

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

(1) fds: el conjunto de descriptores de archivos que se supervisarán y los eventos que se supervisarán.

struct pollfd *fds;

struct pollfd {
    int fd; /* 文件描述符 */
    short events; /* 请求的事件 */
    short revents; /* 返回的事件 */
};

fd es el descriptor de archivo que se va a monitorear. Si fd no es válido, el evento de monitoreo de eventos no es válido y revens devuelve 0. event es el evento a monitorear y el tipo de eventos que se pueden monitorear:

POLLIN 有数据可以读取。
POLLPRI 有紧急的数据需要读取。
POLLOUT 可以写数据。
POLLERR 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起。
POLLNVAL 无效的请求。
POLLRDNORM 等同于 POLLIN

(2) nfds: el número de descriptores de archivos que la función de sondeo supervisará.

(3) tiempo de espera: tiempo de espera, en ms

(4) Valor de retorno: devuelve el número de estructuras pollfd que no son 0 en el campo revens, es decir, el número de descriptores de archivo que tienen eventos o errores; 0: tiempo de espera; -1: ha ocurrido un error y errno está configurado como tipo de error.

3. función epoll

       Las funciones tradicionales de selección y encuesta tendrán el problema de la baja eficiencia a medida que aumenta el número de fd monitoreados, y la función de encuesta debe recorrer todos los descriptores cada vez para verificar los descriptores listos, lo cual es una pérdida de tiempo. La función epoll es manejar esto Diseñado para grandes problemas de concurrencia.

(1) Cree un identificador de epoll. Simplemente ingrese un valor mayor que 0 para el tamaño. El valor de retorno es el identificador de epoll. Si es -1, la creación falló.

int epoll_create(int size)

(2) Agregue descriptores de archivo y eventos monitoreados que necesitan ser monitoreados, devuelva 0 para éxito y -1 para falla.

int epoll_ctl(int epfd,
    int op,
    int fd,
    struct epoll_event *event)

dfpe: manija epoll para ser operada.

op: establecer dfpe

EPOLL_CTL_ADD 向 epfd 添加文件参数 fd 表示的描述符。
EPOLL_CTL_MOD 修改参数 fd 的 event 事件。
EPOLL_CTL_DEL 从 epfd 中删除 fd 描述符

fd: el descriptor de archivo que se va a supervisar.

evento: el tipo de evento a monitorear.

struct epoll_event {
    uint32_t events; /* epoll 事件 */
    epoll_data_t data; /* 用户数据 */
}

La variable eventos representa los eventos a monitorear, parámetros opcionales:

EPOLLIN 有数据可以读取。
EPOLLOUT 可以写数据。
EPOLLPRI 有紧急的数据需要读取。
EPOLLERR 指定的文件描述符发生错误。
EPOLLHUP 指定的文件描述符挂起。
EPOLLET 设置 epoll 为边沿触发,默认触发模式为水平触发。
EPOLLONESHOT 一次性的监视,当监视完成以后还需要再次监视某个 fd,那么就需要将
fd 重新添加到 epoll 里面

(3) Esperando que ocurra el evento

int epoll_wait(int epfd,
    struct epoll_event *events,
    int maxevents,
    int timeout)

epfd: epoll para esperar

events: una matriz que apunta a la estructura epoll_event.Cuando ocurre un evento, el kernel de Linux completará los eventos y la persona que llama puede determinar qué eventos han ocurrido basándose en los eventos.

maxevents: tamaño de la matriz de eventos

timeout: período de tiempo de espera.

Cuatro: Ejemplo

1. Árbol de dispositivos

/ {

    gpioled {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "my-led";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_led>;
        led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
        status = "okay";
};

    key {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "my-key";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_key>;
        key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
        interrupt-parent = <&gpio1>;
        interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
        status = "okay";
    };
}

2. Módulo de accionamiento de botones:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/poll.h>

#define KEY_NUM     1

struct st_keyirq
{
    int gpio;
    int irqnum;
    unsigned char value;
    char name[10];
    irqreturn_t (*handler)(int, void *);
};

struct st_keydev {
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    struct mutex lock;
    atomic_t keyvalue;
    atomic_t releasekey;
    struct timer_list timer;
    struct st_keyirq irqkeydesc[KEY_NUM];
    unsigned char curkeynum;
};
struct st_keydev keydev;
static DECLARE_WAIT_QUEUE_HEAD(key_waitq);


static int key_open(struct inode *inode, struct file *file)
{
    file->private_data = &keydev;

    if(mutex_trylock(&keydev.lock) == 0)
    {
        return -EBUSY;
    }
    return 0;
}

static ssize_t key_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    struct st_keydev *dev = (struct st_keydev *)file->private_data;
    unsigned char releasekey = 0;
    char val = 0;
    int retvalue = 0;

    if(file->f_flags & O_NONBLOCK)
    {
        if(atomic_read(&dev->releasekey) == 0)
            return -EAGAIN;
    }
    else
    {
#if 1
        wait_event_interruptible(key_waitq, atomic_read(&dev->releasekey)); 
#else
        DECLARE_WAITQUEUE(wait, current);
        if(atomic_read(&dev->releasekey) == 0)
        {
            add_wait_queue(&key_waitq, &wait);
            __set_current_state(TASK_INTERRUPTIBLE);
            schedule();
        }
        remove_wait_queue(&key_waitq, &wait);
#endif 
    }

    releasekey = atomic_read(&dev->releasekey);

    if(releasekey)
    {
        val = 1;
        retvalue = copy_to_user(buf, &val, sizeof(val));
        atomic_set(&dev->releasekey, 0);
    }
    else
    {
        val = 0;
        retvalue = copy_to_user(buf, &val, sizeof(val));
    }
    
    return 0;
}

unsigned int key_poll(struct file *filp, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    struct st_keydev *dev = (struct st_keydev *)filp->private_data;

    poll_wait(filp, &key_waitq, wait);

    if(atomic_read(&dev->releasekey))
    {
        mask = POLLIN | POLLRDNORM;
    }
    return mask;
}

static int key_release(struct inode *inode, struct file *file)
{
    struct st_keydev *dev = file->private_data;

    mutex_unlock(&dev->lock);

    return 0;
}


struct file_operations key_fops = {
    .owner      =   THIS_MODULE,
    .open       =   key_open,
    .read       =   key_read,
    .poll       =   key_poll,
    .release    =   key_release, 
};

static irqreturn_t key1_handler(int irq, void *keydev)
{
    struct st_keydev *dev = (struct st_keydev *)keydev;

    dev->curkeynum = 0;
    dev->timer.data = (volatile long)keydev;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(5));
    return IRQ_RETVAL(IRQ_HANDLED);
}

void timer_function(unsigned long data)
{
    unsigned char value;
    unsigned char num;
    struct st_keyirq *keydesc;
    struct st_keydev *dev = (struct st_keydev *)data;

    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num];

    value = gpio_get_value(keydesc->gpio);
    if(value == 0)
    {
        atomic_set(&dev->releasekey, 0);
    }
    else
    {
        wake_up_interruptible(&key_waitq);
        atomic_set(&dev->releasekey, 1);
    }
}

static int key1_init(void)
{
    int retval = 0;
    unsigned char i = 0;

    mutex_init(&keydev.lock);

    keydev.nd = of_find_node_by_path("/key");
    if(keydev.nd == NULL)
    {
        printk("Key node not find!\n");
        return -EINVAL;
    }
    else
    {
        printk("key node find!\n");
    }

    for (i = 0; i < KEY_NUM; i++)
    {
        keydev.irqkeydesc[i].gpio = of_get_named_gpio(keydev.nd, "key-gpio", i);
        if(keydev.irqkeydesc[i].gpio < 0)
        {
            printk("Can't get key%d!\n", i);
            return -EINVAL;
        }
    }

    for (i = 0; i < KEY_NUM; i++)
    {
        memset(keydev.irqkeydesc[i].name, 0, sizeof(keydev.irqkeydesc[i].name));
        sprintf(keydev.irqkeydesc[i].name, "KEY%d", i);
        retval = gpio_request(keydev.irqkeydesc[i].gpio, keydev.irqkeydesc[i].name);
        if(retval < 0)
        {
            printk("request failed!\n");
            return -EINVAL;
        }
        retval = gpio_direction_input(keydev.irqkeydesc[i].gpio);
        if(retval < 0)
        {
            printk("set input failed!\n");
            return -EINVAL;
        }
        keydev.irqkeydesc[i].irqnum = irq_of_parse_and_map(keydev.nd, i);

        printk("key%d:gpio=%d, irqnum=%d\n", i, keydev.irqkeydesc[i].gpio, keydev.irqkeydesc[i].irqnum);
    }

    keydev.irqkeydesc[0].handler = key1_handler;
    for (i = 0; i < KEY_NUM; i++)
    {
        /* code */
        retval = request_irq(keydev.irqkeydesc[i].irqnum, keydev.irqkeydesc[i].handler,
                                IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, keydev.irqkeydesc[i].name, &keydev);
        if(retval < 0)
        {
            printk("irq %d request failed!\n", keydev.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    if(keydev.major)
    {
        keydev.devid = MKDEV(keydev.major, 0);
        register_chrdev_region(keydev.devid, 1, "key");
    }
    else
    {
        alloc_chrdev_region(&keydev.devid, 0, 1, "key");
        keydev.major = MAJOR(keydev.devid);
        keydev.minor = MINOR(keydev.devid);
    }
    printk("keydev major : %d minor : %d\n", keydev.major, keydev.minor);

    keydev.cdev.owner = THIS_MODULE;
    cdev_init(&keydev.cdev, &key_fops);
    cdev_add(&keydev.cdev, keydev.devid, 1);

    keydev.class = class_create(THIS_MODULE, "key");
    keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, "key");

    init_timer(&keydev.timer);
    keydev.timer.function = timer_function;

    return 0;
}

static void key1_exit(void)
{
    unsigned char i = 0;

    del_timer_sync(&keydev.timer);
    for (i = 0; i < KEY_NUM; i++)
    {
        /* code */
        free_irq(keydev.irqkeydesc[i].irqnum, &keydev);
    }
    
    device_destroy(keydev.class, keydev.devid);
    class_destroy(keydev.class);

    cdev_del(&keydev.cdev);
    unregister_chrdev_region(keydev.devid, 1);
}

module_init(key1_init);
module_exit(key1_exit);
MODULE_LICENSE("GPL");

3. Módulo impulsor de luz LED

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define LEDCNT      1
#define LEDNAME     "led"

struct st_dev {
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    int gpio_num;
    struct mutex lock,timelock;
    // struct timer_list timer;
    // atomic_t ledval;
};

struct st_dev leddev;
// atomic_t timeinit = ATOMIC_INIT(1);;

// void timer_function(unsigned long data)
// {
//     struct st_dev *dev = (struct st_dev *)data;
    
//     atomic_set(&dev->ledval, 1);
//     mod_timer(&dev->timer, jiffies + msecs_to_jiffies(500));
// }

static int led_open(struct inode *inode, struct file *file)
{
    file->private_data = &leddev;

    if(mutex_trylock(&leddev.lock) == 0)
    {
        return -EBUSY;
    }
    return 0;
}


static ssize_t led_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
    struct st_dev *dev = file->private_data;
    static unsigned char value = 0;
    char retval, timeval;
    char databuff;

    // dev->timer.data = (volatile long)file->private_data;
    // if(atomic_read(&timeinit))
    // {
    //     atomic_set(&timeinit, 0);
    //     mod_timer(&dev->timer, jiffies + msecs_to_jiffies(500));
    // }
    retval = copy_from_user(&databuff, data, len);
    if(databuff == 1)
    {
        // timeval = atomic_read(&dev->ledval);
        // if(timeval)
        // {
        //     value = !value;
        //     gpio_set_value(dev->gpio_num, value);
        //     atomic_set(&dev->ledval, 0);
        // }
        gpio_set_value(dev->gpio_num, 0);
    }
    else
    {
        gpio_set_value(dev->gpio_num, 1);
    }
    
    return 0;
}       

static int led_release(struct inode *inode, struct file *file)
{
    struct st_dev *dev = file->private_data;

    mutex_unlock(&dev->lock);

    return 0;
}

struct file_operations led_fops = {
    .owner      =   THIS_MODULE,
    .open       =   led_open,
    .write      =   led_write,
    .release    =   led_release,
};

static int led_init(void)
{
    int retval = 0;

    mutex_init(&leddev.lock);

    leddev.nd = of_find_node_by_path("/gpioled");
    if(leddev.nd == NULL)
    {
        printk("gpioled node not find!\n");
        return -EINVAL;
    }
    else
    {
        printk("gpioled node find!\n");
    }

    leddev.gpio_num = of_get_named_gpio(leddev.nd, "led-gpio", 0);
    if(leddev.gpio_num < 0)
    {
        printk("Can't get led-gpio!\n");
        return -EINVAL;
    }
    printk("led-gpio : %d\n",leddev.gpio_num);

    retval = gpio_request(leddev.gpio_num, "led");
    if(retval < 0)
    {
        printk("request failed!\n");
        return -EINVAL;
    }

    retval = gpio_direction_output(leddev.gpio_num, 1);
    if(retval < 0)
    {
        printk("set output failed!\n");
        return -EINVAL;
    }

    if(leddev.major)
    {
        leddev.devid = MKDEV(leddev.major, 0);
        register_chrdev_region(leddev.devid, LEDCNT, LEDNAME);
    }
    else
    {
        alloc_chrdev_region(&leddev.devid, 0, LEDCNT, LEDNAME);
        leddev.major = MAJOR(leddev.devid);
        leddev.minor = MINOR(leddev.devid);
    }
    printk("leddev major : %d minor : %d\n", leddev.major, leddev.minor);

    leddev.cdev.owner = THIS_MODULE;
    cdev_init(&leddev.cdev, &led_fops);

    cdev_add(&leddev.cdev, leddev.devid, LEDCNT);

    leddev.class = class_create(THIS_MODULE, LEDNAME);
    leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDNAME);
    
    // init_timer(&leddev.timer);
    // leddev.timer.function = timer_function;

    return 0;
}

static void led_exit(void)
{
    // del_timer_sync(&leddev.timer);
    device_destroy(leddev.class, leddev.devid);
    class_destroy(leddev.class);

    cdev_del(&leddev.cdev);
    unregister_chrdev_region(leddev.devid, LEDCNT);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

4. Aplicación

#include "sys/stat.h"
#include "sys/types.h"
#include "unistd.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "poll.h"
#include "sys/time.h"
#include "linux/ioctl.h"


#define LED_ON  1
#define LED_OFF 0

int main(int argc, char const *argv[])
{
    int fd1, fd2;
    int ret = 0;
    char keyval, ledval;
    struct pollfd fds;
    char val = 0;
    if(argc != 3)
    {
        printf("Error Usage : ./app /dev/key /dev/led\n");
    }

    fd1 = open(argv[1], O_RDWR | O_NONBLOCK);
    if(fd1 < 0)
    {
        printf("%s can't open!\n", argv[1]);
        return -1;
    }

    fd2 = open(argv[2], O_RDWR);
    if(fd2 < 0)
    {
        printf("%s can't open!\n", argv[2]);
        return -1;
    }

    fds.fd = fd1;
    fds.events = POLLIN;

    while (1)
    {
        ret = poll(&fds, 1, 500);

        if(ret)
        {
            read(fd1, &keyval, sizeof(keyval));
            if(keyval == LED_ON)
            {
                val = !val;
            }
            if(val == LED_ON)
            {
                ledval = LED_ON;
                write(fd2, &ledval, sizeof(ledval));
            }
            else if(val == LED_OFF)
            {
                ledval = LED_OFF;
                write(fd2, &ledval, sizeof(ledval));
            }
        }
        else if (ret == 0)
        {

        } 
    }

    close(fd2);
    close(fd1);

    return 0;
}

 

Supongo que te gusta

Origin blog.csdn.net/qq_34968572/article/details/103877404
Recomendado
Clasificación