首页 > 解决方案 > 未调用 Linux 自定义设备驱动程序探测和初始化函数

问题描述

我在 Vivado 中为 Xilinx SoC 板构建了一个自定义硬件配置,并使用 petalinux 创建了一个自定义驱动程序来控制硬件逻辑。似乎在运行insmod命令之后,驱动程序从未被初始化并且->probe()函数未被调用。

我是这个领域的新手,想知道是否有人遇到过类似的问题,并且有一些关于在哪里检查以及检查什么以查看问题所在的指示。任何建议都会非常有帮助!

dmesg将驱动程序插入内核后运行命令:

root@plzwork3:/proc/device-tree/amba_pl@0/simpleMultiplier@a0000000# dmesg 
[ 3351.680317] <1>Hello module world.
[ 3351.683735] <1>Module parameters were (0xdeadbeef) and "default"

Vivado 创建的设备树实体:

/ {
    amba_pl: amba_pl@0 {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges ;
        simpleMultiplier_0: simpleMultiplier@a0000000 {
            clock-names = "axiliteregport_aclk";
            clocks = <&zynqmp_clk 71>;
            compatible = "xlnx,simpleMultiplier-1.0";
            reg = <0x0 0xa0000000 0x0 0x10000>;
            xlnx,axiliteregport-addr-width = <0x4>;
            xlnx,axiliteregport-data-width = <0x20>;
        };
    };
}; 

设备驱动代码片段:

#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
    { .compatible = "xlnx,simpleMultiplier-1.0", }, 
    { /* end of list */ },
};

完整的设备驱动程序代码:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>

#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/fs.h>

/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");

MODULE_DESCRIPTION("simpmod - loadable module template generated by petalinux-create -t modules");
#define DRIVER_NAME "simpmod"
#define CLASS_NAME "CLASS_TUT"

static char ker_buf[100];
static int currLen = 0;
// Parts of the math operation (2,2,+) is 2+2
static int operand_1 = 0;
static int operand_2 = 0;
static int result = 0;
static struct class *driver_class = NULL;
static dev_t first;
static struct cdev c_dev; // Global variable for the character device
static struct device *ourDevice;

// Pointer to the IP registers
volatile unsigned int *regA;
volatile unsigned int *regB;
volatile unsigned int *regC;

// Structure to hold device specific data
struct simpmod_local {
    int irq;
    unsigned long mem_start;
    unsigned long mem_end;
    void __iomem *base_addr;
};

// Character callbacks prototype
static int dev_open(struct inode *inod, struct file *fil);
static ssize_t dev_read(struct file *fil, char *buf, size_t len, loff_t *off);
static ssize_t dev_write(struct file *fil, const char *buf, size_t len, loff_t *off);
static int dev_release(struct inode *inod, struct file *fil);


static struct file_operations fops = {
        .read=dev_read,
        .write=dev_write,
        .open=dev_open,
        .release=dev_release,
};

static irqreturn_t simpmod_irq(int irq, void *lp){
    printk("simpmod interrupt\n");
    return IRQ_HANDLED;
}

static int simpmod_probe(struct platform_device *pdev){
    struct resource *r_irq; /* Interrupt resources */
    struct resource *r_mem; /* IO mem resources */
    struct device *dev = &pdev->dev;
    static struct simpmod_local *lp = NULL;
    int rc = 0;

    printk("Device Tree Probing\n");

    // Get data of type IORESOURCE_MEM(reg-addr) from the device-tree
    // Other types defined here:
    // http://lxr.free-electrons.com/source/include/linux/ioport.h#L33
    r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!r_mem) {
        dev_err(dev, "invalid address\n");
        return -ENODEV;
    }

    // Allocate memory (continuous physical)to hold simpmod_local struct
    lp = (struct simpmod_local *) kmalloc(sizeof(struct simpmod_local), GFP_KERNEL);
    if (!lp) {
        printk("Cound not allocate simpmod device\n");
        return -ENOMEM;
    }

    dev_set_drvdata(dev, lp);

    // Save data on simpmod_local strucutre
    lp->mem_start = r_mem->start;
    lp->mem_end = r_mem->end;

    // Ask the kernel the memory region defined on the device-tree and
    // prevent other drivers to overlap on this region
    // This is needed before the ioremap
    if (!request_mem_region(lp->mem_start,
                lp->mem_end - lp->mem_start + 1,
                DRIVER_NAME)) {
        dev_err(dev, "Couldn't lock memory region at %p\n",
            (void *)lp->mem_start);
        rc = -EBUSY;
        goto error1;
    }

    // Get an virtual address from the device physical address with a
    // range size: lp->mem_end - lp->mem_start + 1
    lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    if (!lp->base_addr) {
        dev_err(dev, "simpmod: Could not allocate iomem\n");
        rc = -EIO;
        goto error2;
    }
    // ****************** NORMAL Device diver *************************
    // register a range of char device numbers
    if (alloc_chrdev_region(&first, 0, 1, "Leonardo") < 0){
        printk(KERN_ALERT "alloc_chrdev_region failed\n");
        return -1;
    }

    // Create class (/sysfs)
    driver_class = class_create(THIS_MODULE, CLASS_NAME);
    if (driver_class == NULL) {
        printk(KERN_ALERT "Create class failed\n");
        unregister_chrdev_region(first, 1);
        return -1;
    }

    ourDevice = device_create(driver_class, NULL, first, NULL, "tutDevice");
    if ( ourDevice == NULL){
        printk(KERN_ALERT "Create device failed\n");
        class_destroy(driver_class);
        unregister_chrdev_region(first, 1);
        return -1;
    }

    // Create a character device /dev/tutDevice
    cdev_init(&c_dev, &fops);
    if (cdev_add(&c_dev, first, 1) == -1){
        printk(KERN_ALERT "Create character device failed\n");
        device_destroy(driver_class, first);
        class_destroy(driver_class);
        unregister_chrdev_region(first, 1);
    return -1;petalinux-config -c rootfs
    }

    // Create the attribute file on /sysfs/class/CLASS_TUT/ called
    // parCrtl and isBusy


    // Get data of type IORESOURCE_IRQ(interrupt) from the device-tree
    r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (!r_irq) {
        printk("no IRQ found\n");
        printk("simpmod at 0x%08x mapped to 0x%08x\n",
            (unsigned int __force)lp->mem_start,
            (unsigned int __force)lp->base_addr);

        // Configuring pointers to the IP registers
        regA = (unsigned int __force)lp->base_addr + 0x10;
        regB = (unsigned int __force)lp->base_addr + 0x18;
        regC = (unsigned int __force)lp->base_addr + 0x26;
        printk("regA: 0x%08x\n",(unsigned int)regA);
        printk("regB: 0x%08x\n",(unsigned int)regB);
        printk("regC: 0x%08x\n",(unsigned int)regC);
        return 0;
    }
    lp->irq = r_irq->start;

    rc = request_irq(lp->irq, &simpmod_irq, 0, DRIVER_NAME, lp);
    if (rc) {
        dev_err(dev, "testmodule: Could not allocate interrupt %d.\n",
            lp->irq);
        goto error3;
    }
    return 0;
error3:
    free_irq(lp->irq, lp);
error2:
    release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
error1:
    kfree(lp);
    dev_set_drvdata(dev, NULL);
    return rc;
}

static int simpmod_remove(struct platform_device *pdev){
    struct device *dev = &pdev->dev;
    struct simpmod_local *lp = dev_get_drvdata(dev);
    free_irq(lp->irq, lp);
    release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    kfree(lp);
    dev_set_drvdata(dev, NULL);
    return 0;
}

// Indicate which type of hardware we handle on this case(simpleAlu-1.0)
#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
    { .compatible = "xlnx,simpleMultiplier-1.0", }, 
    { /* end of list */ },
};
MODULE_DEVICE_TABLE(of, simpmod_of_match);
#else
# define simpmod_of_match
#endif


static struct platform_driver simpmod_driver = {
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = simpmod_of_match,
    },
    .probe      = simpmod_probe,
    .remove     = simpmod_remove,
};

static int __init simpmod_init(void)
{
    printk("<1>Simple device driver.\n");
  printk("Hussam was here, and he init the module\n");

    return platform_driver_register(&simpmod_driver);
}

static void __exit simpmod_exit(void)
{
    platform_driver_unregister(&simpmod_driver);
    printk(KERN_ALERT "Goodbye module world.\n");
}

static int dev_open(struct inode *inod, struct file *fil){
    printk(KERN_ALERT "Character device opened\n");
    return 0;
}

// Just send to the user a string with the value of result
static ssize_t dev_read(struct file *fil, char *buf, size_t len, loff_t *off){
    // Return the result only once (otherwise a simple cat will loop)
    // Copy from kernel space to user space
    printk(KERN_ALERT "Reading device rx: %d\n",(int)len);
    int n = sprintf(ker_buf, "%d\n", *regC);
    // Copy back to user the result (to,from,size)
    copy_to_user(buf,ker_buf,n);
    printk(KERN_ALERT "Returning %s rx: %d\n",ker_buf,n);
    return n;
}

// Parse the input stream ex: "50,2,*" to some operand variables.
static ssize_t dev_write(struct file *fil, const char *buf, size_t len, loff_t *off){
    // Get data from user space to kernel space
    copy_from_user(ker_buf,buf,len);
    sscanf (ker_buf,"%d,%d,%c",&operand_1,&operand_2);
    ker_buf[len] = 0;
    // Change the IP registers to the parsed operands (on rega and regb)
    *regA = (unsigned int)operand_1;
    *regB = (unsigned int)operand_2;
    printk(KERN_ALERT "Receiving math operation <%d %d>\n",operand_1,operand_2);
    return len;
}

static int dev_release(struct inode *inod, struct file *fil){
    printk(KERN_ALERT "Device closed\n");
    return 0;
}

module_init(simpmod_init);
module_exit(simpmod_exit);

标签: linuxlinux-device-driverxilinxvivadopetalinux

解决方案


问题是,设备树中的(兼容)名称与代码中的名称不匹配:

设备树

compatible = "simple-bus";

代码:

#ifdef CONFIG_OF
    static struct of_device_id simpmod_of_match[] = {
    { .compatible = "xlnx,simpleMultiplier-1.0", }, 
    { /* end of list */ },
};

放置您的设备树:

 compatible = "xlnx,simpleMultiplier-1.0";

两个条目应该匹配!现在应该调用您的“探针”功能!安德烈亚斯


推荐阅读