커널 모듈 생성

커널 모듈 생성

처음으로 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/디렉토리를 의미했을 것입니다.

하드웨어 검색으로 인해 커널 모듈이 자동으로 로드되지 않거나 다른 모듈이 이에 의존하는 경우 해당 디렉토리에 원하는 모듈 이름이 포함된 텍스트 파일을 생성하여 부팅 시 로드되도록 모듈을 구성할 수 있습니다. 로드하려면 한 줄에 하나의 이름을 입력하세요. 원하는 대로 파일 이름을 지정할 수 있지만 접미사가 있어야 합니다 .conf.

두 번째는장치 드라이버 개발에 대한 프로그래밍 질문이므로 다음 위치에 배치해야 합니다.스택 오버플로대신에.

관련 정보