gpio驱动模版,查询方式(非阻塞方式) 访问驱动程序

73 阅读2分钟
erDiagram
module_init ||--o{ gpio_drv_init : init

gpio_drv_init ||--|{ setup_timer : init_timer
setup_timer ||--|{ key_timer_expire : irq_function
key_timer_expire ||--|{ gpio_get_value : gpio_get_value
gpio_get_value ||--|{ put_key : put_key

gpio_drv_init ||--|{ add_timer : start_timer
add_timer ||--|{ key_timer_expires : update_timer_expires

gpio_drv_init ||--|{ request_irq : register_irq
request_irq ||--|{ gpio_key_isr : irq_function
gpio_key_isr ||--|{ mod_timer : update_timer_expires

gpio_drv_init ||--|{ register_chrdev : register_chrdev
register_chrdev ||--|{ gpio_operations_drv : file_operations
gpio_operations_drv ||--|{ read : gpio_drv_read
read ||--|{ is_key_buf_empty_true : return_-EINVAL
read ||--|{ get_key : get_key
get_key ||--|{ copy_to_user : copy_to_user

gpio_drv_init ||--|{ class_create : class_create
gpio_drv_init ||--|{ device_create : device_create

module_exit ||--o{ gpio_drv_exit : exit
gpio_drv_exit ||--o{ device_destroy : device_destroy
gpio_drv_exit ||--o{ class_destroy : class_destroy
gpio_drv_exit ||--o{ unregister_chrdev : unregister_chrdev
gpio_drv_exit ||--o{ free_irq : free_irq
gpio_drv_exit ||--o{ del_timer : del_timer

应用程序

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


int main(int argc, char **argv) {
    int fd;
    int len;
    int key;
    if (argc < 1) {
        printf("Usage: %s </dev/xxx>\n", argv[0]);
		return -1;
    }
    fd = open(argv[0], O_RDWR);
    if (fd < 0)
	{
		printf("open %s err\n", argv[1]);
		return -1;
	}
    while (1) {
        len = read(fd, &key, sizeof(key));
        if (len == sizeof(key)) {
            printf("key: %d", key);
        } else {
            printf("read err %d\n", len);
        }
    }
    return 0;
}

驱动程序

#include "asm-generic/fcntl.h"
#include "asm/gpio.h"
#include "asm/stat.h"
#include "asm/uaccess.h"
#include "linux/err.h"
#include "linux/export.h"
#include "linux/kdev_t.h"
#include "linux/stddef.h"
#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>
#include <linux/timer.h>

static int major = 0;
static struct class *gpio_class;

struct gpio_desc {
    int gpio;
    int irq;
    char *name;
    int key;
    struct timer_list key_timer;
};

static struct gpio_desc gpios[2] = {
    {131, 0, "gpio_100ask_1", 1, },
    {132, 0, "gpio_100ask_2", 2, },
};

/** 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#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 void key_timer_expire(unsigned long data) {
    struct gpio_desc *gpio_desc = (struct gpio_desc *)data;

    int val = gpio_get_value(gpio_desc->gpio);
    /** 引脚key = 1, 引脚值 = 1, key = 1000 0001 */
    int key = (gpio_desc->key) | (val << 8); 
    put_key(key);
}

static irqreturn_t gpio_key_isr(int irq, void *dev_id) {
    struct gpio_desc *gpio_desc = dev_id;
    mod_timer(&gpio_desc->key_timer, jiffies + HZ/5);
    return IRQ_HANDLED;
}

static ssize_t gpio_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset) {
    int err;
    int key;
    if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK)) {
        return -EINVAL;
    }

    key = get_key();
    err = copy_to_user(buf, &key, sizeof(key));
    return sizeof(key);
}

/** 应用函数没有用到 */
// static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset){
//     unsigned char ker_buf[2];
//     int err;
//     /** gpio引脚就注册两个 */
//     if (size != 2) {
//         return -EINVAL;
//     }
//     err = copy_from_user(ker_buf, buf, size);
//     if (ker_buf[0] >= sizeof(gpios)/sizeof(gpios[0])) {
//         return -EINVAL;
//     }
//     gpio_set_value(gpios[ker_buf[0]].gpio, ker_buf[1]);
//     return size;
// }

const struct file_operations gpio_operations_drv = {
    .owner = THIS_MODULE,
    .read  = gpio_drv_read,
    // .write = gpio_drv_write, /** 应用函数没有用到 */
};

static int __init gpio_drv_init(void) {
    int i;
    int count = sizeof(gpios) / sizeof(gpios[0]);
    for (i = 0; i < count; i++ ) {

        setup_timer(&gpios[i].key_timer, key_timer_expire, (unsigned long)&gpios[i]);
        gpios[i].key_timer.expires = ~0;
        add_timer(&gpios[i].key_timer);

        gpios[i].irq = gpio_to_irq(gpios[i].gpio);
        request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "template_gpio_irq", &gpios[i]);

    }
    major = register_chrdev(0, "template_gpio_dev", &gpio_operations_drv);

    gpio_class = class_create(THIS_MODULE, "template_gpio_class");
    if (IS_ERR(gpio_class)) {
        unregister_chrdev(major, "template_gpio_dev");
        return PTR_ERR(gpio_class);
    }

    device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "template_gpio_device");
    return 0;
}

static void __exit gpio_drv_exit(void) {
    int i;
    int count = sizeof(gpios) / sizeof(gpios[0]);

    device_destroy(gpio_class, MKDEV(major, 0));

    class_destroy(gpio_class);

    unregister_chrdev(major, "template_gpio_dev");

    for (i = 0; i < count; i++) {
        free_irq(gpios[i].irq, &gpios[i]);

        del_timer(&gpios[i].key_timer);
    }
}

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);