记录一种在内核空间向用户空间通知中断的方法

记录一种在内核空间向用户空间通知中断的方法


参考文章:Linux驱动实践:中断处理函数如何【发送信号】给应用层?

0.前言

  最近在项目中遇到一个需求,需要将一个设备的中断状态通知到用户态的一个指定程序中,该设备的整体架构如下:
在这里插入图片描述
  CPU通过SPI与设备通信,在设备中存在一个ISR控制单元,当设备发生中断时,会在ISR单元中给对应的寄存器置位,CPU可以通过轮询这些寄存器单元来判断这些中断是否发生。但这样做就失去了中断的意义,内核依旧是通过轮询方式来扫描中断。
  在原先的实现方案中,SPI device通过内核通用接口挂载成了一个内核设备spidev,笔者在此设备基础上,重新在内核中创建了一个影子设备,用来实现用户态程序的读写接口,包括open()、release()、ioctl()接口等,但笔者在后续尝试为这段ISR寄存器地址申请内核中断时,发现官方驱动似乎存在一些问题,会出现设备无法挂载,或挂载后无法正常感知中断的结果。
  所以笔者在同事的帮助下,重新做了一个解决方案,SPI device依旧通过内核通用接口挂载成内核设备,但不创建影子设备,用户态直接操作原设备;此外,为这段ISR地址单独实现一个platform driver,将地址重映射到内存地址中,这样当中断发生时,通过内核的netlink函数通知指定的端口。

1.代码实现

1)内核设备驱动实现

spidev.h:

#ifndef __SPIDEV_H__
#define __SPIDEV_H__

#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/acpi.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/uaccess.h>
#include <linux/phy.h>
#include <net/dsa.h>

struct spidev_switch{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device *parent;

    void __iomem * vir_base_addr;
    unsigned long spi_base_addr;
    unsigned long mem_size;

    struct spi_device *spidev;
    uint32_t		    speed_hz;
    struct mutex		spi_api_lock;

    struct mii_bus*     stmmac_mii;
    int			irq;
    struct gpio_irq_dev *gpioDev;

};

typedef struct{
    uint32_t addr;
    uint32_t *data;
    uint32_t regCnt;
    uint32_t burstType;
    uint32_t crcFlag;
    int32_t ret;
}switch_reg_t;


struct gpio_irq_dev {
	struct gpio_desc *gpiod;
	int irq;
    char irq_name[10];
    char irq_drv_name[20];
}gpio_irq_dev_t;

#define  SWI_DEVICE_NAME    "spi-dev" 
#define  SWI_DEVICE_CNT     1
#define  SWI_IOC_MAGIC      'k'

#define  DRV_NAME            "2404-spi"
#define  DRV_VERSION         "0.1.1"
#define  GPIO_INTR_NUM   3

#define SPI_MODE_MASK		(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
				| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
				| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
				| SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \
				| SPI_RX_QUAD | SPI_RX_OCTAL)

#define WITCH_IOC_SYS_BUS_REG_RD	    	_IOWR(SWI_IOC_MAGIC, 8, switch_reg_t)
#define WITCH_IOC_SYS_BUS_REG_WR	    	_IOWR(SWI_IOC_MAGIC, 9, switch_reg_t)
#define WITCH_IOC_SPI_INNER_REG_RD	    	_IOWR(SWI_IOC_MAGIC, 12, switch_reg_t)
#define WITCH_IOC_SPI_INNER_REG_WR	    	_IOWR(SWI_IOC_MAGIC, 13, switch_reg_t)
#define WITCH_IOC_SPI_BURST_REG_RD	    	_IOWR(SWI_IOC_MAGIC, 14, switch_reg_t)
#define WITCH_IOC_SPI_BURST_REG_WR	    	_IOWR(SWI_IOC_MAGIC, 15, switch_reg_t)
#define SWITCH_IOC_GPIO_ISR_EN    	    	_IOWR(SWI_IOC_MAGIC, 16, switch_reg_t)
#endif

spidev.c:

#include "spidev.h"
#include "my-netlink.h"
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/irq.h>

static int spi_module_open (struct inode *node, struct file *filp)
{
    struct cdev* cdev_t;
    cdev_t = node->i_cdev;
    filp->private_data = container_of(cdev_t, struct spidev_switch, cdev);
    return 0;
}

static ssize_t spi_module_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
    return 0;
}

static ssize_t spi_module_write(struct file *filp, const char __user *buf, size_t count, loff_t *off)
{
    return 0;
}

static int spi_module_release(struct inode *node, struct file *filp)
{
    return 0;
}

static long int spi_module_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int32_t retval = 0;
    int32_t len;
    switch_reg_t switch_reg;
    uint32_t g_mem_reg_data[256] = {0};

    struct spidev_switch* kgsw = file->private_data;

    spidev_dbg_printf(spidev_LOG_DEBUG,"%s:cmd:%x,arg:%x\n",__func__,cmd,arg);

    switch (cmd) {
    case WITCH_IOC_SYS_BUS_REG_RD:
        len = _IOC_SIZE(cmd);
        retval = copy_from_user(&switch_reg, (const void __user *)arg, len);
        spidev_dbg_printf(spidev_LOG_DEBUG,"%s:retval:%x\n",__func__,retval);
        if (retval == 0){
            switch_reg.ret = spidev_apb_reg_read32(kgsw,switch_reg.addr, \
                            switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);
            retval = copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));
            spidev_dbg_printf(spidev_LOG_DEBUG,"%s:retval:%x\n",__func__,retval);
        }	   
        break;

    case WITCH_IOC_SYS_BUS_REG_WR:
        len = _IOC_SIZE(cmd);
        retval = copy_from_user(&switch_reg, (const void __user *)arg, len);
        if (retval == 0){
            retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));
            if (retval == 0){
                switch_reg.ret = spidev_apb_reg_write32(kgsw,switch_reg.addr, \
                        switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);
                spidev_dbg_printf(spidev_LOG_DEBUG,"%s:retval:%x\n",__func__,retval);
            }
        }	   
        break;

    case WITCH_IOC_SPI_INNER_REG_RD:
        len = _IOC_SIZE(cmd);
        retval = copy_from_user(&switch_reg, (const void __user *)arg, len);
        if (retval == 0){
            switch_reg.ret = spi_inner_reg_read32(kgsw,switch_reg.addr,g_mem_reg_data);
            retval = copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));
        }
        break;

    case WITCH_IOC_SPI_INNER_REG_WR:
        len = _IOC_SIZE(cmd);
        retval = copy_from_user(&switch_reg, (const void __user *)arg, len);
        if (retval == 0){
            retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));
            if (retval == 0){
                switch_reg.ret = spi_inner_reg_write32(kgsw,switch_reg.addr,g_mem_reg_data);
            }
        }
        break;

    case WITCH_IOC_SPI_BURST_REG_RD:
        len = _IOC_SIZE(cmd);
        retval = copy_from_user(&switch_reg, (const void __user *)arg, len);
        if (retval == 0){
            switch_reg.ret = spi_burst_reg_read32(kgsw,switch_reg.addr, \
                            switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);
            retval = copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));
        }
        break;

    case WITCH_IOC_SPI_BURST_REG_WR:
        len = _IOC_SIZE(cmd);
        retval = copy_from_user(&switch_reg, (const void __user *)arg, len);
        if (retval == 0){
            retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));
            if (retval == 0){
                switch_reg.ret = spi_burst_reg_write32(kgsw,switch_reg.addr, \
                        switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);
            }
        }
        break;
    case SWITCH_IOC_GPIO_ISR_EN:
        len = _IOC_SIZE(cmd);
        retval = copy_from_user(&switch_reg, (const void __user *)arg, len);
        if (retval == 0){
            retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));
            if (retval == 0){
                switch_reg.ret = gpio_irq_enable(kgsw, switch_reg.addr);
            }
        }
        break;
    default:
        spidev_dbg_printf(spidev_LOG_ERROR, "%s nuknow cmd type\n",__func__);
        break;

	}

    return retval;
}


static struct file_operations switch_module_opts = {
    .owner = THIS_MODULE,
    .open  = spi_module_open,
    .read  = spi_module_read,
    .write = spi_module_write,
    .unlocked_ioctl = spi_module_ioctl,
    .release = spi_module_release,
};

static int is_dev_creat = 0;
int spidev_dev_creat(struct spidev_switch* kgsw)
{
    int major;
    int ret;

    if(is_dev_creat)
        return 0;

    /*alloc devid*/
    alloc_chrdev_region(&kgsw->devid, 0, SWI_DEVICE_CNT, SWI_DEVICE_NAME);
    major = MAJOR(kgsw->devid);

    /*register char dev*/
    cdev_init(&kgsw->cdev, &switch_module_opts);
    ret = cdev_add(&kgsw->cdev, kgsw->devid, SWI_DEVICE_CNT);
    if (ret) {
        goto del_unrigister;
    }

    /*creat device node*/
    kgsw->class = class_create(THIS_MODULE, SWI_DEVICE_NAME);
    if (IS_ERR(kgsw->class)) {
        goto del_cdev;
    } 

    kgsw->device = device_create(kgsw->class, NULL, kgsw->devid, NULL, SWI_DEVICE_NAME);
    if (IS_ERR(kgsw->device)) {
        goto destroy_class;
    }

    find_spi_dev(kgsw);
    of_parse_spidev_reg_addr(kgsw);
    is_dev_creat = 1;

    return 0;

destroy_class:
    device_destroy(kgsw->class, kgsw->devid);
del_cdev:
    cdev_del(&kgsw->cdev);
del_unrigister:
    unregister_chrdev_region(kgsw->devid, SWI_DEVICE_CNT);

    is_dev_creat = 0;
    return -EIO;
}

static irqreturn_t switch_2404_isr_func(int irq, void *dev_id)
{
    //printk(KERN_NOTICE "Enter switch 2404 irq(%d) function\n", irq);
    struct spidev_switch*  kgsw = (struct spidev_switch*)dev_id;
    netlink_msg_t nlg;
    memset(&nlg, 0, sizeof(netlink_msg_t));
    
    void __iomem* vir_addr2 = 0x30800068 - kgsw->spi_base_addr  + kgsw->vir_base_addr;

    writel(1,vir_addr2);
    nlg.cmd_type = REPORT_IRQ_TO_USER;
    nlg.irq_type = SWITCH_INTERRUPT_TYPE;
    nlg.irq_number = irq;
    printk(KERN_NOTICE "Kernel netlink send to user: irq_type(0x%x)  irq_number(%x)\n", nlg.irq_type, nlg.irq_number);
    myspi_send_to_usr_msg(&nlg);
	return IRQ_HANDLED;
}

static irqreturn_t myspi_gpio_isr_func(int irq, void *dev_id)
{
    netlink_msg_t nlg;
    memset(&nlg, 0, sizeof(netlink_msg_t));
    //disable_irq_nosync(irq);
    nlg.cmd_type = REPORT_IRQ_TO_USER;
    nlg.irq_type = GPIO_INTR_TYPE;
    nlg.irq_number = irq;
    printk(KERN_NOTICE "Kernel netlink send to user: irq_type(0x%x)  irq_number(%x)\n", nlg.irq_type, nlg.irq_number);
    myspi_send_to_usr_msg(&nlg);
	return IRQ_HANDLED;
}

static int myspi_gpio_irq_init(struct spidev_switch* kgsw)
{
    int rc;
    struct gpio_irq_dev *gpio_dev = NULL;
    struct device *dev = kgsw->parent;
    int index = 0;

    gpio_dev = devm_kzalloc(dev, sizeof(*gpio_dev) * GPIO_INTR_NUM, GFP_KERNEL);
    if (!gpio_dev)
    {
        dev_err(dev, "no memory.\n");
        return -ENOMEM;
    }
            
    for (index = 0; index < GPIO_INTR_NUM; index++)
    {
        sprintf(gpio_dev[index].irq_name, "%d", index);
        sprintf(gpio_dev[index].irq_drv_name, "%d-gpio-irq", index);

        gpio_dev[index].gpiod = devm_gpiod_get(dev, gpio_dev[index].irq_name, GPIOD_IN);

        if (IS_ERR(gpio_dev[index].gpiod)) {
            rc = PTR_ERR(gpio_dev[index].gpiod);
            if (rc != -EPROBE_DEFER)
                dev_err(dev, "error getting gpio (%d)\n", rc);
            goto irq_err;
        }

        gpio_dev[index].irq = gpiod_to_irq(gpio_dev[index].gpiod);
        if (gpio_dev[index].irq < 0)
        {
            goto irq_err;
        } 

        rc = devm_request_irq(dev, gpio_dev[index].irq, myspi_gpio_isr_func, IRQF_TRIGGER_RISING, gpio_dev[index].irq_drv_name, &gpio_dev[index]); 
        if (rc)
        {
            dev_err(dev, "unable to claim IRQ %d\n", gpio_dev[index].irq);
            goto irq_err;
        }
        else
        {
            printk(KERN_NOTICE " gpio-irq%d %s request success!\n", gpio_dev[index].irq, gpio_dev[index].irq_drv_name);
        }
    }

    kgsw->gpioDev = gpio_dev;
    return 0;

irq_err:
    devm_kfree(dev, gpio_dev);
    return rc;
}

static int spidev_probe(struct platform_device *pdev)
{
	struct spidev_switch*  kgsw;
	int ret = 0;

	kgsw = devm_kzalloc(&pdev->dev, sizeof(struct spidev_switch), GFP_KERNEL);
	kgsw->parent = &pdev->dev;

	kgsw->irq = platform_get_irq(pdev, 0);
	if (kgsw->irq < 0)
    {
        dev_err(&pdev->dev, "irq number invalid\n");
        goto err_out;
    }

	ret = spidev_dev_creat(kgsw);
	if (ret < 0) {
		dev_err(&pdev->dev, "device create fail!\n");
		goto err_out;
	}    

	ret = request_irq(kgsw->irq, switch_2404_isr_func, IRQF_SHARED, dev_name(&pdev->dev), &kgsw->devid);
	if (ret < 0 && ret != -ENOTCONN) {
		dev_err(&pdev->dev, "can not get IRQ\n");
		goto err_out;
	}

    platform_set_drvdata(pdev, kgsw);
    myspi_gpio_irq_init(kgsw);
    myspi_netlink_init();

    spidev_dbg_printf(spidev_LOG_INFO, "%s probe done\n", DRV_NAME);
	return 0;

err_out:
    return -ENXIO;
}

static int spidev_remove(struct platform_device *pdev)
{
    struct spidev_switch*  kgsw = dev_get_drvdata(&pdev->dev);
    printk(KERN_NOTICE " Delete device files\n");
    device_destroy(kgsw->class, kgsw->devid);

    printk(KERN_NOTICE " Delete class files\n");
    class_destroy(kgsw->class);

    printk(KERN_NOTICE " Deregister the character device driver\n");
    cdev_del(&kgsw->cdev);
    
    //Indicates device numbers for deregistering applications
    unregister_chrdev_region(kgsw->devid, SWI_DEVICE_CNT);
  
    myspi_netlink_exit();
    free_irq(kgsw->irq, &kgsw->devid);
    is_dev_creat = 0;
    return 0;
}

static const struct of_device_id spidev_switch_of_match[] = {
	{ .compatible = "my-spidev" },
	{ },
};

MODULE_DEVICE_TABLE(of, spidev_switch_of_match);

static struct platform_driver spidev_driver = {
	.probe = spidev_probe,
	.remove = spidev_remove,
	.driver = {
		.name = "spidev switch driver",
		.of_match_table = spidev_switch_of_match,
	},
};

static int __init spidev_switch_init(void)
{
	printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);
	return platform_driver_register(&spidev_switch_driver);
}

static void __exit spidev_switch_exit(void)
{
	printk(KERN_NOTICE DRV_NAME ": version %s unloaded\n", DRV_VERSION);
	platform_driver_unregister(&spidev_switch_driver);
}

module_init(spidev_switch_init);
module_exit(spidev_switch_exit);

MODULE_DESCRIPTION("Driver for myspi switch");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL v2");

2)消息通知实现

my_netlink.h:

#ifndef __MYSPI_SWITCH_NETLINK_H__
#define __MYSPI_SWITCH_NETLINK_H__

#define NETLINK_IRQ_REPORT_USER      (MAX_LINKS-1)
#define NETLINK_DEFAULT_PORT_ID      0
#define NETLINK_DEFAULT_SEQ          0
#define NETLINK_DEFAULT_FLAGS        0
#define NETLINK_USER_PORT            100


#define INTERRUPT_TYPE        0x24040000
#define PHY_GPIO_INTR_FMC0_TYPE      0x24040001
#define PHY_GPIO_INTR_FMC1_TYPE      0x24040002
#define PHY_GPIO_INTR_FMC2_TYPE      0x24040003

enum {
    ASYNC_READ_CMD = 1,
    ASYNC_WRITE_CMD = 2,
    SHARE_MEM_REG_UPDATE = 3,
    SHARE_MEM_REG_UPDATE_STOP = 4,
    REPORT_IRQ_TO_USER = 5,
};

typedef struct netlink_msg
{
    int  cmd_type;
    int  irq_type;
    int  irq_number;               
}netlink_msg_t;

int myspi_netlink_init(void);
void myspi_netlink_exit(void);
int myspi_send_to_usr_msg(netlink_msg_t* reg);
#endif

my_netlink.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include "myspi_switch_netlink.h"


static struct sock *nlsk = NULL;

int myspi_send_to_usr_msg(netlink_msg_t* msg)
{
    struct sk_buff *nl_skb;
    struct nlmsghdr *nlh;
    int ret;

    //creat sk_buff
    nl_skb = nlmsg_new(sizeof(netlink_msg_t), GFP_ATOMIC);//what time to free;
    if(!nl_skb)
    {
        printk("netlink alloc failure\n");
        return -1;
    }

    // set netlink head msg
    // 填充nlmsg报文头,并最终将报文填充到sk_buff发送出去
    nlh = nlmsg_put(nl_skb, NETLINK_DEFAULT_PORT_ID, NETLINK_DEFAULT_SEQ, NETLINK_IRQ_REPORT_USER, sizeof(netlink_msg_t), NETLINK_DEFAULT_FLAGS); 
    if(nlh == NULL)
    {
        printk("nlmsg_put failaure \n");
        nlmsg_free(nl_skb);
        return -1;
    }

    //send msg; nl_skb will free automatic
    memcpy(nlmsg_data(nlh), msg, sizeof(netlink_msg_t));
    ret = netlink_unicast(nlsk, nl_skb, NETLINK_USER_PORT, MSG_DONTWAIT); // 指定端口号,端口号是唯一识别目的地址的标识,广播非阻塞式发送

    return ret;
}

static void netlink_rcv_msg(struct sk_buff *skb)
{
    struct nlmsghdr *nlh = NULL;
    netlink_msg_t *reg_data = NULL;

    if(skb->len >= nlmsg_total_size(0))
    {
        nlh = nlmsg_hdr(skb); // 取出报文头
        reg_data = NLMSG_DATA(nlh); // 取出报文内容
        if(reg_data)
        {
            //print_regdata(__func__,__LINE__,reg_data);
        }
    }
}

static struct netlink_kernel_cfg cfg = { 
        .input  = netlink_rcv_msg, /* set recv callback */
};


int myspi_netlink_init(void)
{
    /* create netlink socket */
    printk(" Kernel netlink create");
    nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_IRQ_REPORT_USER, &cfg);
    if(nlsk == NULL)
    {   
        printk("Kernel netlink create error!\n");
        return -1; 
    }   
    return 0;
}

void myspi_netlink_exit(void)
{
    printk(" Kernel netlink exit!\n");
    if (nlsk){
        netlink_kernel_release(nlsk); /* release ..*/
        nlsk = NULL;
    }   
}

3)测试程序

test.c:

#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_REG_ASYNC_RW     (MAX_LINKS-1)    //31
#define NETLINK_USER_PORT        100

enum {
    ASYNC_READ_CMD = 1,
    ASYNC_WRITE_CMD = 2,
    SHARE_MEM_REG_UPDATE = 3,
    SHARE_MEM_REG_UPDATE_STOP = 4,
    REPORT_IRQ_TO_USER = 5,
};

typedef struct netlink_msg
{
    struct nlmsghdr hdr;
    int             cmd;
    int  irq_type;
    int  irq_number;     
}netlink_msg_t;

typedef struct netlink_handle
{
    int skfd;
    struct sockaddr_nl saddr;
    struct sockaddr_nl daddr; 
} netlink_handle_t;

static netlink_handle_t async_netlink_handle;

netlink_handle_t* get_async_netlink_handle(void)
{
    return &async_netlink_handle;
}

int switch_netlink_init(void)
{
    int skfd;
    struct sockaddr_nl* saddr; //saddr 表示源端口地址,daddr表示目的端口地址
    struct sockaddr_nl* daddr;
    netlink_handle_t* handle;
    handle = get_async_netlink_handle();

    saddr = &(handle->saddr);
    daddr = &(handle->daddr);

    /* 创建NETLINK socket */
    skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_REG_ASYNC_RW);
    if(skfd == -1)
    {
        perror("create socket error\n");
        return -1;
    }

    memset(saddr, 0, sizeof(struct sockaddr_nl));
    saddr->nl_family = AF_NETLINK; //AF_NETLINK
    saddr->nl_pid = NETLINK_USER_PORT;  //端口号(port ID) 
    saddr->nl_groups = 0;
    if(bind(skfd, (struct sockaddr *)saddr, sizeof(struct sockaddr_nl)) != 0)
    {
        perror("bind() error\n");
        close(skfd);
        return -1;
    }

    memset(daddr, 0, sizeof(struct sockaddr_nl));
    daddr->nl_family = AF_NETLINK;
    daddr->nl_pid = 0; // to kernel 
    daddr->nl_groups = 0;

    handle->skfd = skfd;
    return 0;
}

int switch_recv_reg_msg(void)
{
    int ret;
    netlink_msg_t recv_msg;
    socklen_t len;
    netlink_handle_t* handle;
    handle = get_async_netlink_handle();
    
    memset(&recv_msg, 0, sizeof(netlink_msg_t));
    len = sizeof(struct sockaddr_nl);
    ret = recvfrom(handle->skfd, &recv_msg, sizeof(netlink_msg_t), 0, (struct sockaddr *)&(handle->daddr), &len);

    if(!ret)
    {
        printf("%s,%d, err\n",__func__,__LINE__);
        return -1;
    }

    switch(recv_msg.cmd){
        case REPORT_IRQ_TO_USER:
            printf("User netlink recv from kernel: irq_type(%x), irq_number(%d)\n",recv_msg.irq_type, recv_msg.irq_number);
        break;
        default:
            printf("Unknown command\n");
        break;
    }
    return 0;
}


int main(int argc, char *argv[]){

    if (fork() == 0)
    {
        printf("Create a child process, pid = %d\n", getpid());
        int ret = 0;

        ret = switch_netlink_init();
        if (ret)
        {
            return ret;
        }

        while(1)
        {
            switch_recv_reg_msg();
        }
        printf("main end\n");
    }
    else
    {
        printf("Father process, pid = %d\n", getpid());
        return;
    }
}

2.解析

在设备驱动文件中,实现了一个platform子系统的设备驱动,通过指定的compatible来匹配,并实现了设备的probe、remove、ioctl操作等接口,并且在创建内核设备节点时,为设备申请了spi设备中断和gpio中断,并初始化了一个netlink通信端口。当设备发生中断时,会触发回调函数中的netlink接口,向指定的用户端口发送中断号以及中断类型,这样就可以在用户程序中进行后续操作。
测试小程序中主要是测试netlink的接收,在while(1)循环中一直监听等待内核驱动的信息。

注:此文中仅为大致的实现框架,仅供参考,具体设备的详细实现流程需根据实际情况进行修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值