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";
};
};
};
};
-
/dts-v1/
- ushbu majburiy direktiva dts sintaksisining versiyasini belgilaydi. Kompilyator (dtc) bu bilan fayl formatini aniqlaydi va shunga moslab blob faylga olib o’tadi. -
/plugin/
- Bu direktiva mavjud dts iyerarxiyasiga qo’shimcha kiritilayotganini bildiradi. Odatda bu jarayon mavjud device tree ustiga qo’shimcha qilish uchun ishlatiladi. Bunda asl dts faylni o’rgartirmasdan uni keygaytirish va unga qo’shimchalar kiritish mumkin. -
compatible
- bunda device tree qaysi platformaga mos ekanligi kiritiladi. Yuqoridagi misolda broadcom chiplari uchun misol keltirilgan bo’lsa dts ning boshqa qismlari ham (pinlar, interfeyslar, protokollar) aynan shu chip xususiyatlaridan kelib chiqib shakllantiriladi. -
fragment@0
- Fragmentlar odatda mavjud device treening muayyan joyiga yangilik (patch) qo’shadi. Yuqoridagi misolga asosan biz yadroga yangi qurilma (gpioled@0
) qo’shmoqchimiz. -
gpios
- bu device treedagi GPIO kontrollerga label bo’yicha murojaat qilishni bildirib yuqoridagi misolda brcm chipningGPIO17
qismiga murojaat qiladi. -
status
- bu qurilma statusini belgilaydi, odatda dts orqali qurilma faolligini boshqarish mumkin.
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