Модуль ядра Linux никогда не достигает функций ->probe()

Резюме

Я хочу написать простой модуль ядра Linux, который будет инициализировать GPIO на основе информации, полученной из дерева устройств. Но в настоящее время мой модуль никогда не входит в функцию зонда. Чтобы разобрать его, я создал минимальный пример без части GPIO. Что я хочу видеть в этом минимальном модуле ядра, так это то, что функции probe и remove вызываются правильно.

Аппаратное обеспечение и ядро

Я использую Raspberry Pi 3 со следующим ядром:

$ uname -a
Linux raspberrypi 5.10.92-v7+ #1514 SMP Mon Jan 17 17:36:39 GMT 2022 armv7l GNU/Linux

Наложение дерева устройств

Вот код моего наложения дерева устройств:

/dts-v1/;
/plugin/;
/ {
        compatible = "raspberrypi,3-model-b-plusbrcm,bcm2837";
        fragment@0 {
        target-path = "/";
        __overlay__ {
                jo_test {
                        compatible = "j4l,testdev";
                        status = "enabled";
                        label = "Test";
                        };
                };
        };
};

Я компилирую его с помощью следующей команды:

dtc -W  no-unit_address_vs_reg -I dts -O dtb -o testoverlay.dtbo testoverlay.dts

Затем я загружаю его:

sudo dtoverlay -d . testoverlay.dtbo

Теперь я вижу свой оверлей в /proc/device-tree, и значения также правильно назначены:

$ sudo ls /proc/device-tree/jo_test
compatible  label  name  status
$ for afile in $(sudo ls /proc/device-tree/jo_test); do echo Name: $afile; sudo cat /proc/device-tree/jo_test/$afile; echo ""; done
Name: compatible
j4l,testdev
Name: label
Test
Name: name
jo_test
Name: status
enabled

Модуль ядра

Вот мой минимальный модуль ядра Linux. Я использовал слайды из Дерево устройств для чайников в качестве эталона ([Вы можете найти это здесь][1], слайды 16-18)/ [1]: https://bootlin.com/pub/conferences/2014/elc/petazzoni-device-tree-dummies/petazzoni-device-tree-dummies.pdf

#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/gpio.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>

/* Meta Information */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johannes 4 GNU/Linux");
MODULE_DESCRIPTION("Module creates a folder and file in procfs and implements read and write callbacks");

static int dt_probe(struct platform_device *pdev);
static int dt_remove(struct platform_device *pdev);

static struct of_device_id my_ids[] = {
    {
        .compatible = "j4l,testdev",
    }, { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_ids);

static struct platform_driver my_driver = {
    .probe = dt_probe,
    .remove = dt_remove,
    .driver = {
        .name = "my_driver",
        .of_match_table = my_ids,
    },
};

static int dt_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        const struct of_device_id *of_id = of_match_device(my_ids, dev);

        printk("dt_test - Now I am in dt_probe\n");
        if (!of_id) {
                printk("test_dt - Something went wrong...\n");
                return -1;
        }

        return 0;
}

static int dt_remove(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        const struct of_device_id *of_id = of_match_device(my_ids, dev);

        printk("dt_test - Now I am in dt_remove\n");
        if (!of_id) {
                printk("test_dt - Something went wrong...\n");
        } else {
                printk("dt_test - of_id->name: %s\n",of_id->name);
                printk("dt_test - of_id->compatible: %s\n",of_id->compatible);
        }
        return 0;
}

/**
 * @brief This function is called, when the module is loaded into the kernel
 */
static int __init my_init(void)
{
        int error;
        printk("dt_test - Module Init\n");

        error = platform_driver_register(&my_driver);
        printk("dt_test: error=%d\n", error);
        if (error) {
                printk("dt_test - Error probing \n");
                return error;
        }

        return 0;
}

/**
 * @brief This function is called, when the module is removed from the kernel
 */
static void __exit my_exit(void)
{
        printk("dt_test - Removing module\n");
        platform_driver_unregister(&my_driver);
}

module_init(my_init);
module_exit(my_exit);

А вот мой Makefile:

obj-m += dt_test.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Запустите мой пример

Скомпилировав модуль, вставив DT-оверлей, а затем Модуль ядра, я вижу в логе ядра следующее:

$ sudo insmod dt_test.ko
$ dmesg | tail -n 5
[   33.755037] cam1-reg: disabling
[   33.755061] cam-dummy-reg: disabling
[  811.832947] dt_test: loading out-of-tree module taints kernel.
[  811.833341] dt_test - Module Init
[  811.833643] dt_test: error=0

Итак, мой модуль никогда не входит в функцию dt_probe(), иначе я должен увидеть вывод «Теперь я в dt_probe». И я не знаю, что означает сообщение "загрузка модуля вне дерева". Похоже модуль не может найти совместимый раздел в дереве устройств...

Со второй попытки я скопировал скомпилированный оверлей в /загрузка/оверлеи и добавил оверлей в /boot/config.txt с dtoverlay=тестоверлей. Оверлей появляется в /proc/дерево устройств, но у меня такое же поведение при загрузке модуля...

Знаете ли вы, что я делаю неправильно, или у вас есть какие-либо советы, как я могу это отладить?

"что я делаю не так...?" -- status = "enabled" не является допустимым назначением для этого свойства DT.

sawdust 07.05.2022 21:34

Спасибо за подсказку. Я удалил status = "enabled";, но все равно вижу то же поведение.

johannes4linux 08.05.2022 12:58

Не удаляйте свойство статуса, сделайте его статус = "хорошо" и проверьте

Varun 09.05.2022 10:47

@Varun Отсутствующее свойство статуса должно вести себя так же, как и статус = "хорошо".

Ian Abbott 10.05.2022 16:21
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я заработал. Удаление статуса = "включено" с помощью статуса = "хорошо" исправило это. Проблема заключалась в том, что у меня была скопирована более ранняя версия OVerlay в /boot/overlay, а в /boot/config/ мой оверлей был включен. Итак, при каждой загрузке загружалась фальшивая версия моего оверлея. Но после удаления оверлея из /boot/overlay и из /boot/config все заработало. Мой результат можно найти здесь: https://github.com/Johannes4Linux/Linux_Driver_Tutorial/tree/main/20_dt_probe

Другие вопросы по теме