Хотели ли вы когда нибудь написать свой драйвер, для операционной системы? Думаю многие задумывались об этом, так вот, наконец вам предоставился случай. Продолжаем изучение малинки, на примере ногодрыга, на уровне ядра.

Не смотря на громкое вступление, если вы имели дело с микроконтроллерами, то скорее всего ничего нового в общем то здесь не узнаете :) Но все таки, есть некие отличия. Вспомним, что из себя представляет периферия микроконтроллера, например GPIO. Итак, есть некая задача, настроить ножку на выход и установить ее состояние 1 или 0. Как минимум, для настройки у вас будет 2 регистра, допустим DR — настройка ножки вход или выход и OR — настройка уровня на ножке. Еще могут быть всякие разные дополнительные, например включить/выключить тактирование и т.п.

Пока что сосредоточимся на этих двух регистрах. Что из себя представляет регистр? Для нас это как переменная, в которую можно писать число. В зависимости от этого числа, периферия будет как то настроена. Так в чем отличие переменной от регистра? В том, что регистры всегда имеют определенный адрес, т.е. если вы знаете адрес регистра, то можете жахнуть туда число напрямую, никто вам этого не запретит.

В linux принципы настройки периферии те же самые, пишем число по адресу — ножка настроилась как выход, по другому адресу — подали логическую 1. Однако, если бы любая программа могла случайно обратиться по адресу и бы изменять настройки периферии, то могло бы случиться все что угодно, вплоть до паленого железа. Поэтому, в операционных системах есть два уровня доступа пользовательский и уровень ядра. На пользовательском уровне вы не можете долбиться куда угодно и как угодно, зато на уровне ядра можно.

Тут стоит отвлечься на пример, самого простого модуля ядра, кстати да драйвера в linux называются модули.

#include <linux/module.h>  
#include <linux/kernel.h>   
 
int hello_init(void)
{
    printk(KERN_INFO "Hello world!\n");
    return 0;   
}
 
void hello_exit(void)
{
    printk(KERN_INFO "Exit\n");
}
 
module_init(hello_init);
module_exit(hello_exit);

Этот модуль просто выводит сообщение при загрузке и при выгрузке.

Чтобы скомпилить его понадобится создать такой makefile. Пояснять его сейчас не буду, так как это скорее всего будет в отдельной статье.

obj-m += hello.o

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

После компиляции в папке появится новый файл с расширением ko, вставляем его

sudo insmod hello.ko

И смотрим свое сообщение в логе событий ядра

dmesg

Если понадобится выгрузить модуль

sudo rmmod hello.ko

Итак, после очередного hello world вернемся к нашей проблеме. У нас есть некий GPIO, который мы хотим настроить и включить, дабы потом им управлять. Тут еще раз попытаюсь уточнить цель, предположим вы хотите написать драйвер дисплея wh1602, сама логика инициализации есть на каждом углу, ее не сложно перенести с тех же микроконтроллерных библиотек, т.е. вам остается только настроить правильно GPIO. Если вы вкурите как это делается для одной ноги, то вам ничего не стоит сделать тоже самое для любого другого нужного количества ножек. Поэтому вдупляем, настройка периферии это регистр, регистр это адрес, по адресу долбиться можно напрямую из под ядра.

То что написано ниже можно прочувствовать только плотно работая с дэйтащитом на камень. Открываем на первую малинку и рвем волосы там, где остались
arm_vaddress

В общем он взрывает мозг, тут указано 3 адреса для периферии и в какой долбиться не понятно, но если почитать инфу то становится понятно, что наружу торчит столбец с виртуальной адресацией. Не понятно, почему зачем в разделе GPIO не указаны виртуальные адреса, а указаны которые с шины CPU, т.е. 0x7e, но остальное из того же раздела вполне понятно. Итак, есть регистр GPFSEL, который указывает на то в каком режиме должна быть нога. 0 — вход, 1 — выход. Одни регистр отвечает за настройку 9 ног, пин с 0 по 9 настраивается в GPFSEL0, с 10 по 19 пины в GPFSEL1. Я использовал для тестов 17 ногу, поэтому мой пин относится к GPFSEL1. Его смещение 0x04.

Далее, если поискать есть еще 2 регистра GPSET0 и GPCLR0, один для установки логической 1, другой для логического 0. Каждый регистр используется для контроля с 0 по 31 ножку. Итак, складываем вместе всю картину, есть 3 адреса, в которые мы должны записать соответствующие числа, которые настроят ножку и установят нужное состояние. Все смещения вычисляются относительно базы указанной для GPIO. При загрузке модуля 17 нога включится, при выгрузке выключится.

#include <linux/module.h>
#include <linux/kernel.h>
 
#define PERI_BASE     0xF2000000
#define GPIO_BASE    (PERI_BASE + 0x200000)
#define GPFSEL1       0x04
#define GPSET0        0x1c
#define GPCLR0        0x28
 
volatile int *gpio_set;
volatile int *gpio_reset;
volatile int *fsel;
 
int hello_init(void)
{
   gpio_set = (int *)(GPIO_BASE + GPSET0);
   fsel = (int *)(GPIO_BASE + GPFSEL1);
 
   *gpio_set = 0x00020000;
   *fsel = 0x00200000;
 
  pr_alert("Hello world\n");
  return 0;
}
 
void hello_exit(void)
{
  gpio_reset= (int *)(GPIO_BASE + GPCLR0);
  *gpio_reset= 0x00020000;;
  pr_alert("Goodbye\n");
}
 
module_init(hello_init);
module_exit(hello_exit);
 
MODULE_LICENSE("GPL");

Чувствую, что сейчас любой человек писавший модули плачет кровавыми слезами и схватился за сердце :) Дело в том, что подобный подход применим для микроконтроллеров, все что я хотел показать, что в принципе существует такая возможность. Почему же это плохо? Дело в том, что при подобном подходе, если кто нибудь захочет использовать ту же ножку, которую вы уже задействовали из под другого модуля или из пользовательского пространства, то он сможет это сделать. Чтобы не было такой возможности, используется стандартное API, которое позволяет захватить ножку и дать знать системе, что нога уже используется.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
 
int hello_init(void)
{
  gpio_direction_output(17,1);
  gpio_set_value(17, 1);
  pr_alert("Hello world\n");
  return 0;
}
 
void hello_exit(void)
{
  gpio_set_value(17, 0);
  gpio_free(17);
  pr_alert("Goodbye\n");
}
 
module_init(hello_init);
module_exit(hello_exit);
 
MODULE_LICENSE("GPL");

Как видим, в целом все тоже самое, только без напряга и правильно. Не забываем добавлять MODULE_LICENSE(«GPL»); иначе вы не сможете использовать функции, которые распространяются по этой лицензии, т.е. либа не будет компилиться.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Свежие записи
Последние комментарии
  • Загрузка...
Архивы
Счетчик
Яндекс.Метрика