Icefly 发表于 2017-11-30 17:50:05

GPIO中断如何传递到应用程序

本帖最后由 Icefly 于 2017-12-15 18:01 编辑

朋友们好,我使用rk3399的板子学习GPIO驱动,跑了官网GPIO驱动的例子(http://wiki.t-firefly.com/index.php/Firefly-RK3399/GPIO),能够在内核log里看到相关的中断信息;{:4_225:}
接着我想将该中断信息传递给上层应用程序,在应用程序里做出响应,不知道应该怎么做,新手上路请诸位多多指教。{:4_141:}

从网上了解的的信息来看,有说用异步通信的,也有说使用input框架的,都看不太明白,如果能给个示例代码那最好啦。
参考网上的例子写了个用netlink实现用户空间和内核空间通信的例子,代码大致如下
gpio-firefly.c
/** Driver for pwm demo on Firefly board.** Copyright (C) 2016, Zhongshan T-chip Intelligent Technology Co.,ltd.* Copyright 2006JC.Lin** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>

#include <linux/types.h>
#include <net/sock.h>
#include <linux/netlink.h>

/*
#define NETLINK_TEST 16
struct sock *nl_sk = NULL;
EXPORT_SYMBOL_GPL(nl_sk);
*/
#define NETLINK_USER 22
#define USER_MSG (NETLINK_USER+1)
#define USER_PORT 50
static struct sock *netlinkfd = NULL;
static void *netlinkdata = NULL;
static int netlinkdatalen = 0;

static struct workqueue_struct *queue=NULL;
static struct work_struct my_wq;
static void my_wq_func(struct work_struct *data);

struct firefly_gpio_info
{   
    //struct device *dev;
    //long firefly_param;
    int firefly_gpio;
    int gpio_enable_value;
    int firefly_irq_gpio;   
    int firefly_irq;   
    int firefly_irq_mode;
};

/*
void nl_data_ready(struct sk_buff *__skb)
{
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    u32 pid;
    int rc;
    int len = NLMSG_SPACE(1200);
    char str;
    printk("new_link: data is ready to read.\n");
    skb = skb_get(__skb);
    if(skb->len >= NLMSG_SPACE(0))
    {
      nlh = nlmsg_hdr(skb);
      printk("net_link: recv %s.\n", (char *)NLMSG_DATA(nlh));
      memcpy(str, NLMSG_DATA(nlh), sizeof(str));
      pid = nlh->nlmsg_pid; //pid of sending process
      printk("net_link: pid is %d\n", pid);
      kfree_skb(skb);
      skb = alloc_skb(len, GFP_ATOMIC);
      if(!skb)      
      {
            printk(KERN_ERR "net_link: allocate failed.\n");
            return;
      }
      nlh = nlmsg_put(skb, 0, 0, 0, 1200, 0);
      NETLINK_CB(skb).pid = 0; //from kernel
      memcpy(NLMSG_DATA(nlh), str, sizeof(str));
      printk("net_link: going to send.\n");
      rc = netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
      if(rc < 0)
      {
            printk(KERN_ERR "net_link: can not unicast skb (%d)\n", rc);
      }
      printk("net_link: send is ok.\n");
    }
    return;
}

static int test_netlink(void)
{
    nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE);
    if(!nl_sk)
    {
      printk(KERN_ERR "net_link: Cannot create netlink socket.\n");
      return -EIO;
    }
    printk("net_link: create socket ok.\n");
    return 0;
}
*/

int send_msg(int8_t *pbuf, uint16_t len)
{
    struct sk_buff *nl_skb;
    struct nlmsghdr *nlh;
    int ret;
    nl_skb = nlmsg_new(len, GFP_ATOMIC);
    if(!nl_skb)
    {
      printk("netlink: netlink_alloc_skb error\n");
      return -1;
    }
    nlh = nlmsg_put(nl_skb, 0, 0, USER_MSG, len, 0);
    if(nlh == NULL)
    {
      printk("netlink: nlmsg_put() error\n");
      nlmsg_free(nl_skb);
      return -1;
    }
    memcpy(nlmsg_data(nlh), pbuf, len);
    ret = netlink_unicast(netlinkfd, nl_skb, USER_PORT, MSG_DONTWAIT);
    return ret;
}

static void recv_cb(struct sk_buff *skb)
{
    struct nlmsghdr *nlh = NULL;
    void *data = NULL;
    printk("netlink: skb->len = %u\n", skb->len);
    if(skb->len >= nlmsg_total_size(0))
    {
      nlh = nlmsg_hdr(skb);
      data = NLMSG_DATA(nlh);
      if(data)
      {
            printk("netlink: kernel receive data: %s\n", (int8_t *)data);
            netlinkdata = data;
            netlinkdatalen = nlmsg_len(nlh);
            //send_msg(data, nlmsg_len(nlh));
      }
    }
}

struct netlink_kernel_cfg cfg =
{
    .input = recv_cb,
};


static int test_netlink(void)
{
    netlinkfd = netlink_kernel_create(&init_net, USER_MSG, &cfg);
    if(!netlinkfd)
    {
      printk(KERN_ERR "netlink: can not create a netlink socket!\n");
      return -1;
    }
    printk("netlink: create socket ok.\n");
    return 0;
}


//static struct fasync_struct *gpio_irq_async;

static void my_wq_func(struct work_struct *data)
{
    printk("Enter firefly gpio irq my_wq_func!\n");
    //test_netlink();
    if (netlinkdatalen > 0)
    {
      send_msg(netlinkdata, netlinkdatalen);
      netlinkdatalen = 0;
      printk("firefly netlink send_msg\n");
    }
    printk("Exit firefly gpio irq my_wq_func!\n");
}

static irqreturn_t firefly_gpio_irq(int irq, void *dev_id)
{   
    printk("Enter firefly gpio irq test program!\n");        
    //kill_fasync(&gpio_irq_async, SIGIO,POLL_IN);
    schedule_work(&my_wq);
    return IRQ_HANDLED;
}

/*
static int firefly_gpio_fasync(int fd, struct file *filp, int mode)
{
        //struct scull_pipe *dev = filp->private_data;

        return fasync_helper(fd, filp, mode, &gpio_irq_async);
}*/

/*
static ssize_t gpio_firefly_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct firefly_gpio_info *gpio_info = dev_get_drvdata(dev);
    int len;
    len = sprintf(buf, "%ld\n", gpio_info->firefly_param);
    if (len < 0)
    {
      dev_err(dev, "firefly_gpio_infogpio_firefly_attr_show: Invalid sprintf, len= %d\n", len);
    }
    return len;
}

static ssize_t gpio_firefly_attr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    struct firefly_gpio_info *gpio_info = dev_get_drvdata(dev);
    int ret;
    ret = kstrtol(buf, 10, &gpio_info->firefly_param);
    if (ret != 0)
    {
      dev_err(dev, "firefly_gpio_info gpio_firefly_attr_store: kstrtol failed, ret= %d\n", ret);
    }
    return count;
}

static DEVICE_ATTR(firefly_param, S_IRUGO | S_IWUSR, gpio_firefly_attr_show, gpio_firefly_attr_store);

static struct attribute *firefly_gpio_attrs[] =
{
    &dev_attr_firefly_param.attr,
    NULL,
};

static struct attribute_group firefly_gpio_attr_group =
{
    .name = "firefly_gpio_info",
    .attrs = firefly_gpio_attrs,
};
*/

static int firefly_gpio_probe(struct platform_device *pdev)
{   
    int ret;   
    int gpio;   
    //int error;
    enum of_gpio_flags flag;   
    struct firefly_gpio_info *gpio_info;   
    struct device_node *firefly_gpio_node = pdev->dev.of_node;   
    printk("Firefly GPIO Test Program Probe\n");
       
    gpio_info = devm_kzalloc(&pdev->dev,sizeof(struct firefly_gpio_info *), GFP_KERNEL);        
    if (!gpio_info)
    {         
      dev_err(&pdev->dev, "devm_kzalloc failed!\n");
      return -ENOMEM;        
    }   

    /*
    gpio_info->dev = &pdev->dev;
    platform_set_drvdata(pdev, gpio_info);
    */

    gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-gpio", 0, &flag);   
    if (!gpio_is_valid(gpio))
    {         
      dev_err(&pdev->dev, "firefly-gpio: %d is invalid\n", gpio);         
      return -ENODEV;   
    }   
    if (gpio_request(gpio, "firefly-gpio"))
    {         
      dev_err(&pdev->dev, "firefly-gpio: %d request failed!\n", gpio);         
      gpio_free(gpio);         
      return -ENODEV;   
    }   
    gpio_info->firefly_gpio = gpio;   
    gpio_info->gpio_enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;   
    gpio_direction_output(gpio_info->firefly_gpio, gpio_info->gpio_enable_value);   
    printk("Firefly gpio putout\n");   

    /*
    gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-gpio1", 0, &flag);   
    if (!gpio_is_valid(gpio))
    {         
      dev_err(&pdev->dev, "firefly-gpio1: %d is invalid\n", gpio);         
      return -ENODEV;   
    }   
    if (gpio_request(gpio, "firefly-gpio1"))
    {         
      dev_err(&pdev->dev, "firefly-gpio1: %d request failed!\n", gpio);         
      gpio_free(gpio);         
      return -ENODEV;   
    }   
    gpio_info->firefly_gpio = gpio;   
    gpio_info->gpio_enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;   
    gpio_direction_output(gpio_info->firefly_gpio, gpio_info->gpio_enable_value);   
    printk("Firefly gpio1 putout\n");
    */

    gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-irq-gpio", 0, &flag);   
    printk("firefly:the gpio:%d\n",gpio);   
    if (!gpio_is_valid(gpio))
    {         
      dev_err(&pdev->dev, "firefly-irq-gpio: %d is invalid\n", gpio);         
      return -ENODEV;   
    }         
    gpio_info->firefly_irq_gpio = gpio;   
    gpio_info->firefly_irq_mode = flag;   
    gpio_info->firefly_irq = gpio_to_irq(gpio_info->firefly_irq_gpio);   
    if (gpio_info->firefly_irq)
    {         
      if (gpio_request(gpio, "firefly-irq-gpio"))
      {            
            dev_err(&pdev->dev, "firefly-irq-gpio: %d request failed!\n", gpio);            
            gpio_free(gpio);            
            return IRQ_NONE;         
      }           
      ret = request_irq(gpio_info->firefly_irq, firefly_gpio_irq, flag, "firefly-gpio", gpio_info);           
      if (ret != 0)
      {            
            free_irq(gpio_info->firefly_irq, gpio_info);                   
            dev_err(&pdev->dev, "firefly-gpio Failed to request IRQ: %d\n", ret);         
      }
      queue = create_singlethread_workqueue("firefly-irq-gpio");
      if (!queue)
      {
            return -1;
      }
      INIT_WORK(&my_wq, my_wq_func);   
    }
    /*   
        error = sysfs_create_group(&pdev->dev.kobj, &firefly_gpio_attr_group);
        if (error) {
                dev_err(&pdev->dev, "Unable to export firefly param, error: %d\n",
                        error);
                return error;
        }
    */
    test_netlink();
    return 0;
}

static int firefly_gpio_remove(struct platform_device *pdev)
{
//    sysfs_remove_group(&pdev->dev.kobj, &firefly_gpio_attr_group);
    if (netlinkfd != NULL)
    {
      sock_release(netlinkfd->sk_socket);
      printk(KERN_DEBUG "netlink exit\n");
    }
    return 0;
}

/*
struct file_operations firefly_gpio_fops = {
        .owner =        THIS_MODULE,
        .llseek =        no_llseek,
        .fasync =        firefly_gpio_fasync,
};*/

static struct of_device_id firefly_match_table[] =
{        
    { .compatible = "firefly,rk3399-gpio",},         {}, };
    static struct platform_driver firefly_gpio_driver =
    {        
    .driver = {                
    .name = "firefly-gpio",                
    .owner = THIS_MODULE,                
    .of_match_table = firefly_match_table,        
    },        
    .probe = firefly_gpio_probe,
    .remove = firefly_gpio_remove,
};

static int firefly_gpio_init(void)
{        
    return platform_driver_register(&firefly_gpio_driver);
}

module_init(firefly_gpio_init);

static void firefly_gpio_exit(void)
{        
    platform_driver_unregister(&firefly_gpio_driver);
}

module_exit(firefly_gpio_exit);

MODULE_AUTHOR("linjc <service@t-firefly.com>");
MODULE_DESCRIPTION("Firefly GPIO driver");
MODULE_ALIAS("platform:firefly-gpio");
MODULE_LICENSE("GPL");

用户空间代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>

#define NETLINK_USER 22
#define USER_MSG (NETLINK_USER+1)
#define MSG_LEN 100

#define MAX_PLOAD 100

struct _my_msg
{
        struct nlmsghdr hdr;
        int8_t data;
};

int main(int argc, char **argv)
{
        char *data = "hello kernel";
        struct sockaddr_nl local, dest_addr;
        int skfd;
        struct nlmsghdr *nlh = NULL;
        struct _my_msg info;
        int ret;
        int socklen;
        int recvcount = 0;
        skfd = socket(AF_NETLINK, SOCK_RAW, USER_MSG);
        if(skfd == -1)
        {
                printf("create socket error... %s\n", strerror(errno));
                return -1;
        }
        memset(&local, 0, sizeof(local));
        local.nl_family = AF_NETLINK;
        local.nl_pid = 50;
        local.nl_groups = 0;
        if(bind(skfd, (struct sockaddr *)&local, sizeof(local)) != 0)
        {
                printf("bind() error\n");
                close(skfd);
                return -1;
        }
        memset(&dest_addr, 0, sizeof(dest_addr));
        dest_addr.nl_family = AF_NETLINK;
        dest_addr.nl_pid = 0;
        dest_addr.nl_groups = 0;
        nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
        memset(nlh, 0, sizeof(struct nlmsghdr));
        nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
        nlh->nlmsg_flags = 0;
        nlh->nlmsg_type = 0;
        nlh->nlmsg_seq = 0;
        nlh->nlmsg_pid = local.nl_pid;
        memcpy(NLMSG_DATA(nlh), data, strlen(data));
      while(1)
        {
        ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl));
    if(!ret)
    {
      perror("sendto error1\n");
      close(skfd);
      exit(-1);
    }
    printf("wait kernel msg!\n");
//    while(1)
//    {
    memset(&info, 0, sizeof(info));
    socklen = sizeof(dest_addr);
    ret = recvfrom(skfd, &info, sizeof(struct _my_msg), 0, (struct sockaddr *)&dest_addr, (socklen_t*)&socklen);
    if(!ret)
    {
      perror("recv from kernel error\n");
      close(skfd);
      exit(-1);
    }
    printf("msg receive from kernel: %s, %d\n", info.data, recvcount++);
    if (recvcount%2 == 0)
    {
      system("./light on");
    }
    else
    {
      system("./light off");
    }
    }
    close(skfd);
    free((void *)nlh);
    return 0;
}


mengduo 发表于 2017-12-7 16:36:11

顶一个

Icefly 发表于 2017-12-12 18:19:16

mengduo 发表于 2017-12-7 16:36
顶一个

额,谢谢朋友。对于这个问题你有没有什么建议。

mengduo 发表于 2017-12-14 14:46:46

Icefly 发表于 2017-12-12 18:19
额,谢谢朋友。对于这个问题你有没有什么建议。

我也是新手,需求跟你差不多,目前也在研究,楼主有什么突破吗{:4_105:}

Icefly 发表于 2017-12-14 18:39:11

mengduo 发表于 2017-12-14 14:46
我也是新手,需求跟你差不多,目前也在研究,楼主有什么突破吗

{:4_167:}握握手先,估计问的问题太基础了,所以都没人回答。最近这几天我研究了一下,我认为我想达到的目的是实现内核空间和用户空间的通信,搜了一下其方式又很多种,我本来想尝试用ioctl或者fasync,但由于找不到platform driver里file_operations,所以我又尝试了sysfs和netlink的方式,sysfs尝试失败,原因不明。。;但netlink的方式成功了,回来我把代码贴上来,看看对你有没有帮助。

b166er 发表于 2017-12-25 18:08:38

必须要写内核代码吗?
Linux原生的 sys/class/gpio/gpio??/edge 机制不能用吗?

yinseyingji 发表于 2018-5-17 10:40:18

第一个想到的是用IOCTL,然后一直读,有中断来了就返回,请问博主如何编译程序,我现在写了个hello world 用aarch64-。。。。。。
这个编译器编译不过

freebigfish 发表于 2019-4-17 13:17:28

我顶一个,赞楼主

ooxx 发表于 2019-8-24 13:21:31

安卓的话要往应用层传数据就用JNI,这个android.mk用ndk编so包,然后给Java native调用
页: [1]
查看完整版本: GPIO中断如何传递到应用程序