Asynchronous notifications, the cornerstone of the driver

Asynchronous notification process


The key points start from ②:
② APP registers the signal processing function func for the SIGIO signal. Later, the APP receives SIGIO
When a signal is generated, this function will be automatically called;
③ Tell the driver the PID (process ID) of the APP. This call does not involve the driver.
The core’s file system hierarchy records the PID;
④ Read the driver file Flag;
⑤ Set the FASYNC bit in Flag to 1: When the FASYNC bit changes, it will cause the driver to
The driver’s fasync is called;
⑥⑦ Call faync_helper, which will determine whether to set it based on the value of FAYSNC
button_async->fa_file=Driver file filp:
The driver file filp structure contains the PID set previously.
⑧ APP can do other things;
⑨⑩ When the button is pressed, an interrupt occurs, and the driver’s interrupt service routine is called, which calls
kill_fasync signals;
?After APP receives the signal, its signal processing function is automatically called and can be called inside
The read function reads the keystrokes.

The above are the notes of Teacher Wei Dongshan. A brief description of the process is as follows:
The APP program (process) registers a signal processing function for the signal to be received. The APP program also tells the driver its process number, and sets the FASYNC bit to call the fasync function of the startup program. The fasync_helper function is called in the fasync function to set the pointer button_fasync (a pointer of the self-defined struct fasync_struct type). The brief function of the fasync_helper function:


**Note: **Be sure to set the FASYNC bit to be valid (think of the asynchronous notification switch).
Then call the kill_fasync function in the key interrupt processing function to send the signal. The app program is running. If a key is pressed, the key interrupt processing function will send a signal to the app, and then the app will jump to the signal processing function for execution.

Code

Driver code

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>

struct gpio_key{<!-- -->
int gpio;
struct gpio_desc *gpiod;
int flag;
int irq;
} ;

static struct gpio_key *gpio_keys_100ask;

/* Major device number */
static int major = 0;
static struct class *gpio_key_class;

/* Ring buffer */
#defineBUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

struct fasync_struct *button_fasync;

#define NEXT_POS(x) ((x + 1) % BUF_LEN)

static int is_key_buf_empty(void)
{<!-- -->
return (r == w);
}

static int is_key_buf_full(void)
{<!-- -->
return (r == NEXT_POS(w));
}

static void put_key(int key)
{<!-- -->
if (!is_key_buf_full())
{<!-- -->
g_keys[w] = key;
w = NEXT_POS(w);
}
}

static int get_key(void)
{<!-- -->
int key = 0;
if (!is_key_buf_empty())
{<!-- -->
key = g_keys[r];
r = NEXT_POS(r);
}
return key;
}


static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);

/* Implement the corresponding open/read/write and other functions and fill in the file_operations structure */
static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{<!-- -->
//printk("%s %s line %d\\
", __FILE__, __FUNCTION__, __LINE__);
int err;
int key;
\t
wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
key = get_key();
err = copy_to_user(buf, & amp;key, 4);
\t
return 4;
}

static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
{<!-- -->
printk("%s %s line %d\\
", __FILE__, __FUNCTION__, __LINE__);
poll_wait(fp, & amp;gpio_key_wait, wait);
return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_key_drv_fasync(int fd, struct file *file, int on)
{<!-- -->
if (fasync_helper(fd, file, on, & amp;button_fasync) >= 0)
return 0;
else
return -EIO;
}


/* Define your own file_operations structure */
static struct file_operations gpio_key_drv = {<!-- -->
.owner = THIS_MODULE,
.read = gpio_key_drv_read,
.poll = gpio_key_drv_poll,
.fasync = gpio_key_drv_fasync,
};


static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{<!-- -->
struct gpio_key *gpio_key = dev_id;
int val;
int key;
\t
val = gpiod_get_value(gpio_key->gpiod);
\t

printk("key %d %d\\
", gpio_key->gpio, val);
key = (gpio_key->gpio << 8) | val;
put_key(key);
wake_up_interruptible( & amp;gpio_key_wait);
kill_fasync( & amp;button_fasync, SIGIO, POLL_IN);
\t
return IRQ_HANDLED;
}

/* 1. Get GPIO from platform_device
 * 2. gpio=>irq
 * 3. request_irq
 */
static int gpio_key_probe(struct platform_device *pdev)
{<!-- -->
int err;
struct device_node *node = pdev->dev.of_node;
int count;
int i;
enum of_gpio_flags flag;
\t\t
printk("%s %s line %d\\
", __FILE__, __FUNCTION__, __LINE__);

count = of_gpio_count(node);
if (!count)
{<!-- -->
printk("%s %s line %d, there isn't any gpio available\\
", __FILE__, __FUNCTION__, __LINE__);
return -1;
}

gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
for (i = 0; i < count; i + + )
{<!-- -->
gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, & amp;flag);
if (gpio_keys_100ask[i].gpio < 0)
{<!-- -->
printk("%s %s line %d, of_get_gpio_flags fail\\
", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio);
gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW;
gpio_keys_100ask[i].irq = gpio_to_irq(gpio_keys_100ask[i].gpio);
}

for (i = 0; i < count; i + + )
{<!-- -->
err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", & amp;gpio_keys_100ask[i]);
}

/* Register file_operations */
major = register_chrdev(0, "100ask_gpio_key", & amp;gpio_key_drv); /* /dev/gpio_key */

gpio_key_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
if (IS_ERR(gpio_key_class)) {<!-- -->
printk("%s %s line %d\\
", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "100ask_gpio_key");
return PTR_ERR(gpio_key_class);
}

device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, "100ask_gpio_key"); /* /dev/100ask_gpio_key */
        
    return 0;
    
}

static int gpio_key_remove(struct platform_device *pdev)
{<!-- -->
//int err;
struct device_node *node = pdev->dev.of_node;
int count;
int i;

device_destroy(gpio_key_class, MKDEV(major, 0));
class_destroy(gpio_key_class);
unregister_chrdev(major, "100ask_gpio_key");

count = of_gpio_count(node);
for (i = 0; i < count; i + + )
{<!-- -->
free_irq(gpio_keys_100ask[i].irq, & amp;gpio_keys_100ask[i]);
}
kfree(gpio_keys_100ask);
    return 0;
}


static const struct of_device_id ask100_keys[] = {<!-- -->
    {<!-- --> .compatible = "100ask,gpio_key" },
    {<!-- --> },
};

/* 1. Define platform_driver */
static struct platform_driver gpio_keys_driver = {<!-- -->
    .probe = gpio_key_probe,
    .remove = gpio_key_remove,
    .driver = {<!-- -->
        .name = "100ask_gpio_key",
        .of_match_table = ask100_keys,
    },
};

/* 2. Register platform_driver in the entry function */
static int __init gpio_key_init(void)
{<!-- -->
    int err;
    
printk("%s %s line %d\\
", __FILE__, __FUNCTION__, __LINE__);
\t
    err = platform_driver_register( & amp;gpio_keys_driver);
\t
return err;
}

/* 3. If there is an entry function, there should be an exit function: when the driver is uninstalled, this exit function will be called.
 * Uninstall platform_driver
 */
static void __exit gpio_key_exit(void)
{<!-- -->
printk("%s %s line %d\\
", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister( & amp;gpio_keys_driver);
}


/* 7. Other improvements: provide device information and automatically create device nodes */

module_init(gpio_key_init);`Insert code snippet here`
module_exit(gpio_key_exit);

MODULE_LICENSE("GPL");



Application code

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>

static int fd;
static void sig_func(int sig)
{<!-- -->
int val;
read(fd, & amp;val, 4);
printf("get button : 0x%x\\
", val);
}

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{<!-- -->
int val;
struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
int flags;
\t
/* 1. Judgment parameters */
if (argc != 2)
{<!-- -->
printf("Usage: %s <dev>\\
", argv[0]);
return -1;
}

signal(SIGIO, sig_func);

/* 2. Open the file */
fd = open(argv[1], O_RDWR);
if (fd == -1)
{<!-- -->
printf("can not open file %s\\
", argv[1]);
return -1;
}

fcntl(fd, F_SETOWN, getpid());
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);

while (1)
{<!-- -->
printf("www.100ask.net \\
");
sleep(2);
}
\t
close(fd);
\t
return 0;
}