..

DTS fayllar nima va qanday ishlaydi?

Loyihani qo'llab quvvatlash uchub buyerga bosing

Zamonaviy Linux asosidagi embedded qurilmalar soni kundan kunga oshib bormoqda. Har bir qurilmaning o’ziga xos apparat (hardware) konfiguratsiyasi bo’lishi tabiiy. Operatsion tizim, ayniqsa Linux yadrosi (kernel), ushbu apparatni to’g’ri boshqarishi uchun uning tuzilishini va parametrlarini bilishi kerak. Ana shunday holatlarda DTS fayllar – ya’ni Device Tree Source fayllari – juda muhim rol o’ynaydi.

Device Tree nima?

Device Tree — bu apparat resurslarini ta’riflovchi ma’lumotlar tuzilmasidir. U Linux yadrosiga: qurilmalar qanday manzillarda joylashgan, u qanday turdagi qurilma, qanday parametrlar bilan ishlaydi kabi savollarga javob beradi. Yadro bu ma’lumotlar asosida qurilmalar uchun drayverlarni yuklaydi va sozlaydi.

Bunga ko’ra dts (Device Tree’ning) odam o’qiy oladigan matn ko’rinishidagi faylidir. U apparat haqida tuzilgan iyerarxik tuzilmadagi yozuvlardan iborat bo’ladi. Faylda har bir qurilmalar uchun ehtiyojlarga qarab node kiritiladi va uyerda qurilmaga doir xususiyatlar yoziladi. Shundan so’ng dts fayl yakunida dtb (device tree blob) fayliga kompilatsiya qilinadi, bu esa o’z navbatida Linux yadrosi tomonidan ishlatiladi.

Bu nega kerak?

Avvallari linux yadrosi apparat ta’minotiga moslab kompilatsiya qilinar edi. Bu usul noqulay bo’lib, har bir qurilma uchun alohida yadro yig’ish talab qilinadi. Device tree yordamida esa bitta umumiy yadro ko’plab qurilmalarda ishlashi mumkin bo’ladi va bu jarayonda faqat dts (ya’ni konfiguratsiya) fayli o’zgaradi.

Ish prinsipi

DTS fayllar sintaksisi xuddi json kabi ammo ancha kengaytirilgan holda yoziladi. Unda asosan qaysi chiplar bilan mos kelishi va modulning xususiyatlari kiritiladi.

Masalan quyidagi oddiy led chiroq misolida ko’rsak:

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835";

    fragment@0 {
        target-path = "/";

        __overlay__ {
            mygpioled: gpioled@0 {
                compatible = "test,gpioled";
                gpios = <&gpio 17 0>;
                status = "okay";
            };
        };
    };
};

Kompiliyatsiya qilish:

dtc -I dts -O dtb -o gpio-led.dtbo gpio-led-overlay.dts
sudo cp gpio-led.dtbo /boot/overlays/

Shundan so’ng /boot/config.txt faylning eng oxiriga yadro qurilmani yuklashi uchun quyidagi qo’shimchani kiritish lozim.

dtoverlay=gpio-led

Yoki vaqtinchalik yuklash:

sudo dtoverlay gpio-led.dtbo

Yuqoridagi dts uchun linux modul yaratish:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "gpioled"

static dev_t dev_num;
static struct class *gpioled_class;
static struct cdev gpioled_cdev;
static struct gpio_desc *led_gpio;

static int gpioled_open(struct inode *inode, struct file *file) {
    return 0;
}

static int gpioled_release(struct inode *inode, struct file *file) {
    return 0;
}

static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t len, loff_t *off) {
    char value;

    if (copy_from_user(&value, buf, 1))
        return -EFAULT;

    if (value == '1') {
        gpiod_set_value(led_gpio, 1);
    } else {
        gpiod_set_value(led_gpio, 0);
    }

    return len;
}

static const struct file_operations gpioled_fops = {
    .owner = THIS_MODULE,
    .open = gpioled_open,
    .release = gpioled_release,
    .write = gpioled_write,
};

static int gpioled_probe(struct platform_device *pdev) {
    int ret;

    led_gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
    if (IS_ERR(led_gpio)) {
        dev_err(&pdev->dev, "Failed to get GPIO\n");
        return PTR_ERR(led_gpio);
    }

    ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
    if (ret)
        return ret;

    cdev_init(&gpioled_cdev, &gpioled_fops);
    gpioled_cdev.owner = THIS_MODULE;

    ret = cdev_add(&gpioled_cdev, dev_num, 1);
    if (ret)
        goto unregister_chrdev;

    gpioled_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(gpioled_class)) {
        ret = PTR_ERR(gpioled_class);
        goto del_cdev;
    }

    device_create(gpioled_class, NULL, dev_num, NULL, DEVICE_NAME);

    dev_info(&pdev->dev, "GPIO LED driver probed\n");
    return 0;

del_cdev:
    cdev_del(&gpioled_cdev);
unregister_chrdev:
    unregister_chrdev_region(dev_num, 1);
    return ret;
}

static int gpioled_remove(struct platform_device *pdev) {
    device_destroy(gpioled_class, dev_num);
    class_destroy(gpioled_class);
    cdev_del(&gpioled_cdev);
    unregister_chrdev_region(dev_num, 1);
    return 0;
}

static const struct of_device_id gpioled_dt_ids[] = {
    { .compatible = "test,gpioled" },
    { }
};
MODULE_DEVICE_TABLE(of, gpioled_dt_ids);

static struct platform_driver gpioled_driver = {
    .driver = {
        .name = "gpioled",
        .of_match_table = gpioled_dt_ids,
    },
    .probe = gpioled_probe,
    .remove = gpioled_remove,
};

module_platform_driver(gpioled_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yetimdasturchi");
MODULE_DESCRIPTION("Example GPIO LED Driver");
obj-m += gpioled.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

Modulni kompiliyatsiya qilish va ishga tushirish:

make
sudo insmod gpioled.ko

Moduldan foydalanish uchun tashqi dastur:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

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

    if (argc != 2 || (argv[1][0] != '1' && argv[1][0] != '0')) {
        printf("Usage: %s [1|0]\n", argv[0]);
        return 1;
    }

    fd = open("/dev/gpioled", O_WRONLY);
    if (fd < 0) {
        perror("Failed to open /dev/gpioled");
        return 1;
    }

    val = argv[1][0];
    write(fd, &val, 1);

    close(fd);
    return 0;
}

Foydalanish:

gcc led_control.c -o led_control
sudo ./led_control 1
sudo ./led_control 0