Writing drivers for KolibriOS/ru: Difference between revisions
(Global update of the article, part 1a) |
(Global update of the article, part 1b) |
||
Line 6: | Line 6: | ||
== Вступление == | == Вступление == | ||
'''Предупреждение 0.''' Данная статья описывает написание драйверов для основной ветки Колибри ОС и не включает в себя исторические моменты. Предыдущая версия статьи находится на форуме по [http://board.kolibrios.org/viewtopic.php?p=10987#p10987 ссылке]. | |||
'''Предупреждение 1.''' Прежде чем писать драйвер, хорошо подумайте, нельзя ли обойтись средствами прикладных [http://ru.wikipedia.org/wiki/API API], в частности, функций работы с оборудованием [[SysFn46/ru|46]] и [[SysFn62/ru|62]]. Во-первых, от ошибки в кривом приложении пострадает только это кривое приложение, а кривой драйвер способен без особого труда обрушить всю систему. Во-вторых, для приложений можно вылавливать баги в отладчике [[Mtdbg/ru|MTDBG]], обладающем определёнными возможностями, а для драйверов этот путь закрыт (разве что встроенный отладчик эмулятора [http://ru.wikipedia.org/wiki/Bochs Bochs], но он заведомо непригоден для отладки с реальным железом), так что единственным средством остаётся отладочный вывод на доску отладки [[Board/ru|BOARD]] со всеми недостатками. | '''Предупреждение 1.''' Прежде чем писать драйвер, хорошо подумайте, нельзя ли обойтись средствами прикладных [http://ru.wikipedia.org/wiki/API API], в частности, функций работы с оборудованием [[SysFn46/ru|46]] и [[SysFn62/ru|62]]. Во-первых, от ошибки в кривом приложении пострадает только это кривое приложение, а кривой драйвер способен без особого труда обрушить всю систему. Во-вторых, для приложений можно вылавливать баги в отладчике [[Mtdbg/ru|MTDBG]], обладающем определёнными возможностями, а для драйверов этот путь закрыт (разве что встроенный отладчик эмулятора [http://ru.wikipedia.org/wiki/Bochs Bochs], но он заведомо непригоден для отладки с реальным железом), так что единственным средством остаётся отладочный вывод на доску отладки [[Board/ru|BOARD]] со всеми недостатками. | ||
Line 1,165: | Line 1,167: | ||
== Мьютексы и семафоры == | == Мьютексы и семафоры == | ||
В большинстве случаев для обработки нескольких одновременных вызовов функций необходимы средства синхронизации и блокировки доступа к ресурсам, с которыми работает данная функция. Для обеспечения такой блокировки драйвер импортирует функции, реализующие мьютексы и семафоры | |||
<syntaxhighlight lang="C"> | |||
struct mutex { | |||
struct list_head wait_list; | |||
atomic_t count; | |||
}; | |||
void fastcall mutex_init(struct mutex *lock); | |||
void fastcall mutex_lock(struct mutex *lock); | |||
void fastcall mutex_unlock(struct mutex *lock); | |||
void fastcall init_rwsem(struct rw_semaphore *sem); | |||
void fastcall down_read(struct rw_semaphore *sem); | |||
void fastcall down_write(struct rw_semaphore *sem); | |||
void fastcall up_read(struct rw_semaphore *sem); | |||
void fastcall up_write(struct rw_semaphore *sem); | |||
</syntaxhighlight> | |||
== Работа с подсистемой событий == | == Работа с подсистемой событий == | ||
== Интерфейс работы с дисковой подсистемой == | == Интерфейс работы с дисковой подсистемой == | ||
Ядро предоставляет интерфейс для добавления, удаления и работы с логическими дисками. Данный интерфейс позволяет реализовывать драйвера различных дисковых устройств, вне зависимости от их физического интерфейса, в том числе и виртуальные. <br> | |||
Для добавления нового диска используется функция DiskAdd. Описание передаваемых в неё параметров приведено ниже по тексту.<br> | |||
Для удаления диска используется функция DiskDel, в которую передаётся полученный ранее указатель. Эта функция удаляет диск из единого списка логических дисков. | |||
<syntaxhighlight lang="C"> | |||
void* DiskAdd(DISKFUNC* functions, const char* name, uint32_t userdata, uint32_t flags); | |||
void DiskDel(void* hDisk); | |||
</syntaxhighlight> | |||
TODO!!! | |||
Тут должно быть про callback функции, флаги и DiskMediaChanged. | |||
== Интерфейс работы со встроенными устройствами == | == Интерфейс работы со встроенными устройствами == | ||
Встроенные устройства включают в себя всевозможные устройства, расположенные непосредственно на материнской плате или подключаемым к внутренним шинам, например ISA или PCIe. | Встроенные устройства включают в себя всевозможные устройства, расположенные непосредственно на материнской плате или подключаемым к внутренним шинам, например ISA или PCIe. | ||
=== Прерывания === | === Прерывания === | ||
Прерывания используются многими встроенными устройствами для оповещения драйверов об изменении их состояния, например, для оповещения о подключении сетевого кабеля или нажатие на клавишу клавиатуры. | Прерывания используются многими встроенными устройствами для оповещения драйверов об изменении их состояния, например, для оповещения о подключении сетевого кабеля или нажатие на клавишу клавиатуры. | ||
Для добавления своего обработчика прерывания, драйвер должен вызвать функцию в которую передаётся номер прерывания, указатель на функцию обработчика прерывания и необходимые ей данные в виде 4 байт. | Для добавления своего обработчика прерывания, драйвер должен вызвать функцию в которую передаётся номер прерывания, указатель на функцию обработчика прерывания и необходимые ей данные в виде 4 байт. | ||
<syntaxhighlight lang="C"> | <syntaxhighlight lang="C"> | ||
stdcall | int32_t stdcall AttachIntHandler(uint32_t irq, void* handler, uint32_t userdata); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Обработчик прерывания должен соблюдать CDECL соглашение о вызовах и принимать необходимые ей данные в виде 4 байт. | Обработчик прерывания должен соблюдать CDECL соглашение о вызовах и принимать необходимые ей данные в виде 4 байт. | ||
<syntaxhighlight lang="C"> | <syntaxhighlight lang="C"> | ||
cdecl | int32_t cdecl irq_handler(uint32_t userdata); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Если вызванный обработчик прерывания не обнаружил взаимодействия от настроенного на него контроллера, то обработчик должен вернуть 1 в качестве ответа. В иных случаях обработчик должен вернуть ноль. | Если вызванный обработчик прерывания не обнаружил взаимодействия от настроенного на него контроллера, то обработчик должен вернуть 1 в качестве ответа. В иных случаях обработчик должен вернуть ноль. | ||
=== Шина PCI === | === Шина PCI === | ||
Большинство подключённых к ПК устройств используют шину PCI(PCIe). Для работы с этой шиной драйвера импортируют ряд функций, через которые можно прочесть и записать данные в конфигурационное пространство PCI устройства. Подробнее об этом говорится в статье [http://wiki.kolibrios.org/wiki/PCI/ru PCI]. <br> | |||
Ядро экспортирует как сами функции работы с PCI шиной, так и функцию GetPCIList для получения указателя на список найденных им устройств. Этот список устройств представляет из себя двусвязный список структур PCIDEV. На основе этой структуры можно произвести поиск PCI устройства без обращения к самой шине, что упрощает написание самого драйвера. | |||
<syntaxhighlight lang="asm"> | |||
struct PCIDEV | |||
bk dd ? | |||
fd dd ? | |||
vendor_device_id dd ? | |||
class dd ? | |||
devfn db ? | |||
bus db ? | |||
rb 2 | |||
owner dd ? ; pointer to SRV or 0 | |||
ends | |||
</syntaxhighlight> | |||
Для чтения и записи используется набор функций со схожим интерфейсом. | |||
<syntaxhighlight lang="C"> | |||
uint8_t stdcall PciRead8(uint23_t bus, uint32_t devfn, uint32_t reg); | |||
uint16_t stdcall PciRead16(uint23_t bus, uint32_t devfn, uint32_t reg); | |||
uint32_t stdcall PciRead32(uint23_t bus, uint32_t devfn, uint32_t reg); | |||
void stdcall PciWrite8(uint23_t bus, uint32_t devfn, uint32_t reg, uint32_t value); | |||
void stdcall PciWrite16(uint23_t bus, uint32_t devfn, uint32_t reg, uint32_t value); | |||
void stdcall PciWrite32(uint23_t bus, uint32_t devfn, uint32_t reg, uint32_t value); | |||
PCIDEV* fastcall GetPCIList(); | |||
</syntaxhighlight> | |||
Кроме этих функций существует также функция PciApi, через которую также возможно осуществить чтение и запись конфигурационного пространства. | |||
Интерфейс этой функции повторяет интерфейс системной функции [[SysFn62/ru|62]]. | |||
=== Шина USB === | === Шина USB === | ||
Line 1,189: | Line 1,247: | ||
=== Порты ввода/вывода === | === Порты ввода/вывода === | ||
Драйверы могут взаимодействовать со всеми портами ввода/вывода, но для избежание вредоносного взаимодействия со стороны пользовательского ПО необходимо зарезервировать необходимые порты через | Драйверы могут взаимодействовать со всеми портами ввода/вывода, но для избежание вредоносного взаимодействия со стороны пользовательского ПО необходимо зарезервировать необходимые порты через функцию ReservePortArea. | ||
<syntaxhighlight lang="asm"> | <syntaxhighlight lang="asm"> | ||
;reserve/free group of ports | ;reserve/free group of ports | ||
; * eax = 46 - number function | ; * eax = 46 - number function | ||
Line 1,209: | Line 1,267: | ||
* 80-223 | * 80-223 | ||
* 229-255 | * 229-255 | ||
Многие устройства предоставляют интерфейс работы через спроецированные на память регистры контроллера. Для работы с этими регистрами используется функция MapIoMem, в которую передаётся базовый адрес физической памяти, на который указывает устройство(во многих PCI устройствах такой адрес будет находится в BAR регистрах конфигурационного пространства PCI) размер и флаги страниц памяти. Функция вернёт указатель на базовый адрес в виртуальной памяти либо ноль в случае неудачи. | |||
<syntaxhighlight lang="C"> | |||
void* stdcall MapIoMem(void* base, uint32_t size, uint32_t flags); | |||
</syntaxhighlight> | |||
== Интерфейс взаимодействия с сетевой подсистемой == | == Интерфейс взаимодействия с сетевой подсистемой == | ||
== | == Интерфейс взаимодействия с графической подсистемой == | ||
=== Регистрация аппаратного курсора === | |||
=== Изменение фреймбуфера === | |||
== Остальные импортируемые ядром функции == | == Остальные импортируемые ядром функции == | ||
=== Функция для драйверов мыши === | === Функция для драйверов мыши === | ||
<syntaxhighlight lang="C"> | <syntaxhighlight lang="C"> | ||
stdcall | void stdcall SetMouseData(uint32_t BtnState, uint32_t XMoving, uint32_t YMoving, uint32_t VScroll, uint32_t HScroll); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Функции для драйверов клавиатуры === | === Функции для драйверов клавиатуры === | ||
<syntaxhighlight lang="C"> | <syntaxhighlight lang="C"> | ||
KEYBOARD* stdcall RegKeyboard(KBDFUNC* func, uint32_t userdata); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="C"> | <syntaxhighlight lang="C"> | ||
stdcall | void stdcall DelKeyboard(KEYBOARD* handle); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="C"> | <syntaxhighlight lang="C"> | ||
fastcall | void fastcall SetKeyboardData(uint32_t scancode); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Функции работы с потоками === | === Функции работы с потоками === | ||
=== Функции таймера === | === Функции таймера === | ||
=== Функции вывода в доску отладки === | |||
== Полезные макросы языка fasm и особенности их применения == | == Полезные макросы языка fasm и особенности их применения == |
Revision as of 18:24, 22 October 2023
ВНИМАНИЕ: Данная статья находится в разработке и содержит множество устаревших и непроверенных данных. В данный момент использование этой статьи в качестве справочного материала не рекомендуется.
ПРЕДУПРЕЖДЕНИЕ: Данная статья устарела и переписывается. НЕ ИСПОЛЬЗУЙТЕ ЭТУ СТАТЬЮ.
Вступление
Предупреждение 0. Данная статья описывает написание драйверов для основной ветки Колибри ОС и не включает в себя исторические моменты. Предыдущая версия статьи находится на форуме по ссылке.
Предупреждение 1. Прежде чем писать драйвер, хорошо подумайте, нельзя ли обойтись средствами прикладных API, в частности, функций работы с оборудованием 46 и 62. Во-первых, от ошибки в кривом приложении пострадает только это кривое приложение, а кривой драйвер способен без особого труда обрушить всю систему. Во-вторых, для приложений можно вылавливать баги в отладчике MTDBG, обладающем определёнными возможностями, а для драйверов этот путь закрыт (разве что встроенный отладчик эмулятора Bochs, но он заведомо непригоден для отладки с реальным железом), так что единственным средством остаётся отладочный вывод на доску отладки BOARD со всеми недостатками.
Далее допустим, что вы всё ещё читаете эту статью. Мало ли, может, вы всегда пишете код с первого раза безошибочно (чего только на свете не бывает), или в совершенстве владеете отладкой прямо в мозгу и считаете всякие отладочные средства баловством, или просто считаете, что настоящий мужчина (настоящая леди?) не боится трудностей и несколькими строчками текста вас не напугать.
Предупреждение 2. Драйвера, естественно, тесно связаны с ядром. А в ядро КолибриОС вносятся изменения несколько раз в неделю. Разумеется, большинство изменений никак не касается драйверной подсистемы, но иногда добавляются/исчезают/изменяются важные системные функции, экспортируемые драйверам. Поэтому если вы возьмёте и скомпилируете прилагаемый к статье код, то, возможно, он прямо в таком виде работать не будет. Так что внимательно читайте текст - я постараюсь выделить по возможности все причины неработоспособности в будущем и требуемые модификации. Прилагаемый к статье код рассчитан на ревизию {{#svn_rev:450}}, последнюю на момент написания этих строк (в дистрибутиве 0.6.5.0 работать в таком виде не будет).
Вообще-то основная задача драйверов - обеспечить работу с оборудованием. Но поскольку эта статья ставит своей целью показать принципы работы драйверов, а для реализации основной задачи нужно много кода, работающего именно с железом и не имеющего никакого отношения к драйверной подсистеме, то процесс написания драйвера показан на следующем примере: создадим драйвер, перехватывающий и записывающий все обращения приложений к файловой системе, и управляющую программу, которая получает данные от драйвера и отображает их. В качестве средства разработки используется FASM. Архив к статье находится здесь.
Описание работы с драйверной подсистемой
Драйверная подсистема позволяет загружать драйвера в формате PE и работать с ними. Для загрузки драйверов в ядре придусмотрено 2 сисфункции 68.16 и 68.21 а для управления драйвером сисфункция 68.17. Драйвер должен иметь экспортируемую функцию START, которая должна возвращать хандлер стуктуры драйвера в случае успеха, и ноль в случае, если драйвер не загружен. Для работы с ядром драйверу нужно импортировать некоторые функции ядра, которые экспортируются ядром как core.dll.
Драйвер
Специально для желающих написать свой драйвер предоставляется каркас драйвера. Он находится в svn-репозитории вместе с ядром, точнее, в папке {{#svn:/kernel/trunk/drivers|svn://kolibrios.org/kernel/trunk/drivers}}. Ну что же, давайте посмотрим ({{#svn:/kernel/trunk/drivers/sceletone.asm|sceletone.asm из #450|450}}):
<syntaxhighlight lang="asm">