内核模块的创建

内核模块的创建

我必须第一次创建一个 Linux 驱动程序,我有一些问题,但我没有找到方便的答案。我的目标是创建一个 PWM 驱动程序,感谢 IOCTL,我已经成功创建了一个驱动程序并与他合作,但我还想做两件事,其中一件事我不知道该怎么做。

第一件事:我希望驱动程序在启动时加载,我读了很多有关探测功能的内容,但我不明白它是如何工作的,有些人谈论 module-load.r 那么最好的方法是什么?

第二件事:我必须为使用该驱动程序的设备的每个设备树声明创建许多驱动程序,但是如何为每个实例在 /dev/ 中动态创建文件?

我不知道是否清楚,但我给出了代码和设备树:

#define WR_DUTYCYCLE _IOW('a', 1, int32_t *)
#define WR_FREQ _IOW('a', 2, int32_t *)
#define RD_DUTYCYCLE _IOR('a', 3, int32_t *)
#define RD_FREQ _IOR('a', 4, int32_t *)

#define DEV_CLASS_MODE ((umode_t)(S_IRUGO|S_IWUGO))

/* Meta Information */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Benjamin CLEMENT");
MODULE_DESCRIPTION("Generate a PWM with chosen DUTY CYCLE and FREQUENCY");

/* Variables for device and device class */
static dev_t my_device_nr;
static struct class *my_class;
static struct cdev my_device;

static struct platform_device *pdev;
unsigned long *base_addr;
unsigned long *base_addr;

#define DRIVER_NAME "pwm"
#define DRIVER_CLASS "MyModuleClass"


/**
 * @brief This function is called, when the device file is opened
 */
static int driver_open(struct inode *device_file, struct file *instance) {
    printk("dev_nr - open was called!\n");
    return 0;
}

/**
 * @brief This function is called, when the device file is opened
 */

static int driver_close(struct inode *device_file, struct file *instance) {
    printk("dev_nr - close was called!\n");
    return 0;
}

/**
 * @brief This function is called, when the module use IOCTL for write or read
 */

static long int my_ioctl(struct file *file, unsigned cmd, unsigned long arg){
    
    long int back;                                                                      //Looking for the register
    char *path = "/";                                            
    struct device_node *dt_node;  
    dt_node = of_find_node_by_path(path);
    struct device_node *dt_pwm;
    u32 reg[4]; 
    
    //Looking for base_adress
    if (!dt_node) {                                                              
        printk(KERN_ERR "Failed to find node by path: %s.\n");                   
    } else {                                                                     
            dt_pwm = of_find_node_by_name(dt_node, file->f_path.dentry->d_iname);
            if (!dt_pwm) {                                                              
                printk(KERN_ERR "Failed to find node pwm: %s.\n");                   
            }
            else{
                of_property_read_u32_array(dt_pwm, "reg", reg, 4);
                //printk("Adress GPIO1 : %x", reg[1]); 
                //printk("ADRESS GPIO2 : %x", reg[1] + 8);
            }
    }
    switch(cmd){
        case WR_DUTYCYCLE: 
            writel(arg, ioremap(reg[1], 8));
            //printk("test wrdc %lu\n", arg);
            back = 0;
            break;
        case WR_FREQ:
            writel(arg, ioremap(reg[1] + 8, 8));
            back = 0;
            break;
        case RD_DUTYCYCLE:
            back = readl(ioremap(reg[1], 8));
            printk("Valeur ecrite dc : %d", back);
            break;
        case RD_FREQ:
            back = readl(ioremap(reg[1] + 8, 8));
            printk("Valeur ecrite : %d", back);
            break;
    }
    return back;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = driver_open,
    .release = driver_close,
    .unlocked_ioctl = my_ioctl,
};

/**
 * @brief This function is called at init for permit all users to call this drivers
 */

static char *mydevnode(struct device *dev, umode_t *mode)
  {
     if (mode)
         *mode = DEV_CLASS_MODE;; 
     return NULL;
 }

/**
 * @brief This function is called, when the module is loaded into the kernel
 */
 
 
 
static int __init ModuleInit(void) {
    int retval;
    printk("Hello, Kernel! test2\n");

    /* Allocate a device nr */
    if(alloc_chrdev_region(&my_device_nr, 0, 1, DRIVER_NAME) < 0) {
        printk("Device Nr. could not be allocated!\n");
        return -1;
    }
    printk("read_write - Device Nr. Major: %d, Minor: %d was registered!\n", my_device_nr >> 20, my_device_nr && 0xfffff);

    /* Create device class */
    if((my_class = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) {
        printk("Device class can not be created!\n");
        goto ClassError;
    }

    //Activate for all users
    my_class->devnode = mydevnode;
    
    /* create device file */
    if(device_create(my_class, NULL, my_device_nr, NULL, DRIVER_NAME) == NULL){
        printk("Can not create device file!\n");
        goto FileError;
    }
    /* Initialize device file */
    cdev_init(&my_device, &fops);

    /* Registering device to kernel */
    if(cdev_add(&my_device, my_device_nr, 1) == -1) {
        printk("Registering of device to kernel failed!\n");
        goto AddError;
    }
    
    return 0;
AddError:
    device_destroy(my_class, my_device_nr);
FileError:
    class_destroy(my_class);
ClassError:
    unregister_chrdev_region(my_device_nr, 1);
    return -1;
}

/**
 * @brief This function is called, when the module is removed from the kernel
 */
static void __exit ModuleExit(void) {
    cdev_del(&my_device);
    device_destroy(my_class, my_device_nr);
    class_destroy(my_class);
    unregister_chrdev_region(my_device_nr, 1);
    printk("Goodbye, Kernel\n");
}

module_init(ModuleInit);
module_exit(ModuleExit);

我关心的设备树是这样的:

amba_pl@0 {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges ;
            pwm1@80000000 {
            #gpio-cells = <3>;
            clock-names = "s_axi_aclk";
            clocks = <&zynqmp_clk 71>;
            compatible = "pwm";
            gpio-controller ;
            reg = <0x0 0x80000000 0x0 0x10000>;
            xlnx,all-inputs = <0x1>;
            xlnx,all-inputs-2 = <0x1>;
            xlnx,all-outputs = <0x0>;
            xlnx,all-outputs-2 = <0x0>;
            xlnx,dout-default = <0x00000000>;
            xlnx,dout-default-2 = <0x00000000>;
            xlnx,gpio-width = <0x20>;
            xlnx,gpio2-width = <0x20>;
            xlnx,interrupt-present = <0x0>;
            xlnx,is-dual = <0x1>;
            xlnx,tri-default = <0xFFFFFFFF>;
            xlnx,tri-default-2 = <0xFFFFFFFF>;
        };
            pwm0@80010000 {
            #gpio-cells = <3>;
            clock-names = "s_axi_aclk";
            clocks = <&zynqmp_clk 71>;
            compatible = "pwm";
            gpio-controller ;
            reg = <0x0 0x80010000 0x0 0x10000>;
            xlnx,all-inputs = <0x0>;
            xlnx,all-inputs-2 = <0x0>;
            xlnx,all-outputs = <0x1>;
            xlnx,all-outputs-2 = <0x1>;
            xlnx,dout-default = <0x00000000>;
            xlnx,dout-default-2 = <0x00000000>;
            xlnx,gpio-width = <0x8>;
            xlnx,gpio2-width = <0x20>;
            xlnx,interrupt-present = <0x0>;
            xlnx,is-dual = <0x1>;
            xlnx,tri-default = <0xFFFFFFFF>;
            xlnx,tri-default-2 = <0xFFFFFFFF>;
        };
    };

谢谢你的帮助 !

答案1

第一件事:我希望驱动程序在启动时加载,我读了很多有关探测功能的内容,但我不明白它是如何工作的,有些人谈论 module-load.r 那么最好的方法是什么?

您可能指的是/etc/modules-load.d/目录。

如果由于硬件探测或某些其他模块依赖于它而未自动加载内核模块,则可以通过在该目录中创建一个文本文件来配置要在引导时加载的模块,其中包含模块的名称( s) 您希望加载,每行一个名称。您可以随意命名该文件,但它必须有一个.conf后缀。

你的第二件事是一个关于设备驱动开发的编程问题,所以它应该放在堆栈溢出反而。

相关内容