Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 3842 → Rev 3843

/kernel/trunk/docs/events_subsystem.txt
0,0 → 1,232
Дата последней правки 26/07/2013.
Подсистема событий ядра может понадобиться при написании драйверов и сервисов, работающих в режиме ядра.
Она не имеет отношения к подсистеме событий пользовательского интерфейса.
С точки зрения ядра событие - объект ядра и принадлежит создавшему его потоку.
 
struc EVENT
{
.magic dd ? ; 'EVNT'
.destroy dd ? ; internal destructor
.fd dd ? ; next object in list
.bk dd ? ; prev object in list
.pid dd ? ; owner id. идентификатор владельца (потока)
.id dd ? ; event uid. уникальный идентификатор события (просто номерок)
.state dd ? ; internal flags; см. далее.
.code dd ? ; старший байт класс события, ; следующий байт приоритет
; (будет использоваться только внутри ядра, при чтении всегда 0),
; Чем больше численное значение двойного слова тем важнее событие.
; два младших байта код события.
rd 5 ; .data - точная структура этого поля не определена и зависит
; от поля .code. (Здесь можно передавать какие-то свои данные,
; при необходимости :)
.size = $ - .magic
.codesize = $ - .code
}
 
События реального времени получили класс 0хFF. Пока определёны только:
EVENT.code= ;(Используется в звуковой подсистеме).
RT_INP_EMPTY equ 0xFF000001
RT_OUT_EMPTY equ 0xFF000002
RT_INP_FULL equ 0xFF000003
RT_OUT_FULL equ 0xFF000004
 
 
Флаги поля EVENT.state определены в gui/event.inc.
EVENT_SIGNALED equ 0x20000000 ;Бит 29 событие активно/неактивно;
EVENT_WATCHED equ 0x10000000 ;бит 28, поток-владелец ожидает активации события;
MANUAL_RESET equ 0x40000000 ;бит 30, не деактивировать событие автоматически по получении;
MANUAL_DESTROY equ 0x80000000 ;бит 31, не возвращать событие в список свободных по получении.
 
На момент ревизии 3732 (и далее по тексту то же) определение находится в \kernel\trunk\const.inc
и выглядит так:
 
struct APPOBJ ; common object header
magic dd ? ;
destroy dd ? ; internal destructor
fd dd ? ; next object in list
bk dd ? ; prev object in list
pid dd ? ; owner id
ends
 
struct EVENT APPOBJ
id dd ? ;event uid
state dd ? ;internal flags
code dd ?
rd 5 ; .data
ends
 
Код находится в gui/event.inc.
Сами события как обьекты существуют в памяти ядра в виде двусвязного списка (см. поля .bk и .fd).
При инициализации ядро резервирует память и создает 512 таких обьектов, помещая их в список FreeEvents
(свободных событий). При нехватке событий (все заняты, а нужно ещё) ядро создает ещё 512 свободных
и т.д. Каждый поток имеет свои (двусвязные) списки (в которые может быть помещено событие):
ObjList - список объектов ядра, ассоциированных с этим потоком;
EventList - список событий ядра для потока.
Сами события, физически, при перемещении между списками и смене очередности в списке не перемещаются
и не копируются. Это происходит только благодаря модификации полей .fd и .bk. Принцип работы списков,
как очередей - FIFO. Использутся неблокирующая отправка и блокирующее получение. Адресация - прямая
(у события всегда есть поток-владелец), по идентификатору потока.
 
Жизненый цикл событий определяется флагами при создании. По умолчанию ядро использует значения
MANUAL_RESET = 0 и MANUAL_DESTROY = 0. Такое событие является "одноразовым", и автоматически освобождается
ядром, возвращаясь в список свободных событий после получения.
Событие с флагом MANUAL_DESTROY = 1 после получения переходит в неактивное состояние, но остаётся в списке
объектов потока и может использоваться снова. Событие с флагами MANUAL_DESTROY =1 и MANUAL_RESET = 1
остаётся активным после получения и может быть сброшено вызовом ClearEvent.
 
Пример (вариант) жизненного цикла события из звуковой подсистемы:
Для зукового буфера (их может быть несколько) драйвер создает событие в списке ObjList с помощью
CreateEvent и флагом MANUAL_DESTROY. Далее драйвер вызывает WaitEvent для этого события (ожидает флага
EVENT_SIGNALED в событии) и блокируется, в ожидании запроса на пополнение буфера. Запрос отправляется
с помощью RaiseEvent из другого потока. Отправка (RaiseEvent) и получение (WaitEvent) циклически
повторяются при опустошении буфера. При остановке воспроизведения драйвер деактивирует событие с помощью
ClearEvent.
 
Вообще говоря, структура события приведена здесь только лишь для понимания принципов работы подсистемы.
Самостоятельная работа с полями не приветствуется, ввиду возможных в будущем проблем с совместимостью.
Работа должна производится только через API (функции подсистемы), с доступом только к тем полям, доступ к
которым предоставляет функция. При этом пару "указатель на событие" и "уникальный идентификатор события"
следует рассматривать как один 64-х битный уникальный идентификатор. (Если вы вызвали CreateEvent, напимер,
его нужно запомнить где-нибудь [если это нужно] для дальнейшей работы с событием).
 
Функции для работы с событиями экспортитуемые ядром:
(для драйверов и т.п.; вызываются в режиме ядра)
 
CreateEvent
RaiseEvent
ClearEvent
SendEvent
DestroyEvent
WaitEvent
WaitEventTimeout
GetEvent
Для пользовательских приложений Ф68.14 (GetEvent с обёрткой)
 
---------------------------------------------------------------------------------------------
CreateEvent:
Создаёт новое событие в очереди ObjList текущего потока.
Устанавливает:
EVENT.destroy <= внутренний деструктор по умолчанию;
EVENT.pid <= текущий Process id;
EVENT.id <= уникальный идентификатор;
EVENT.state <= ecx - флаги;
EVENT.code <= [esi], (если esi=0, то не копирует), размер 6*dword;
Возвращает:
eax - указатель на событие или 0 при ошибке.
edx - Event.id.
Портит: eax,ebx,edx,ecx,esi,edi
---------------------------------------------------------------------------------------------
RaiseEvent:
Активирует уже существующее событие (может принадлежать другому потоку) установкой
флага EVENT_SIGNALED. Если необходимо, - устанавливает данные EVENT.code.
Если флаг EVENT_SIGNALED в самом событии уже активен - больше ничего не делает.
Если EVENT_SIGNALED не установлен в самом событии, то он будет установлен, кроме случая
{EVENT_WATCHED в edx=1 и EVENT_WATCHED в событии=0}.
Т.е. при установке EVENT_WATCHED в edx, проверяется, ожидает ли поток-владелец активации
события.
Кроме EVENT_SIGNALED в событии никакие другие флаги не модифицируются.
Принимает:
eax - указатель на событие;
ebx - id, уникальный идентификатор события;
edx - флаги для операции (формат EVENT.state);
EVENT.code <= [esi], (если esi=0, то не копирует), размер 6*dword;
Возвращает: ?
Портит: eax,ebx,edx,ecx,esi,edi .
---------------------------------------------------------------------------------------------
ClearEvent:
Перемещает событие в список ObjList потока-владельца. (Возможно оно там и находилось.)
Сбрасывает флаги EVENT_SIGNALED, EVENT_WATCHED. С остальными полями (.code, .id),
ничего не делает.
Принимает:
eax - указатель на событие;
ebx - id, уникальный идентификатор события.
Возвращает: ?
Портит: eax,ebx,ecx,edi .
---------------------------------------------------------------------------------------------
SendEvent:
Создаёт новое событие в списке событий целевого потока. Устанавливает в событии
флаг EVENT_SIGNALED.
Принимает:
EVENT.pid <= eax - pid, идентификатор целевого потока;
EVENT.code <= [esi], (если esi=0, то не копирует), размер 6*dword;
Возвращает:
eax - указатель на событие или 0 при ошибке.
edx - Event.id. уникальный идентификатор.
Портит: eax,ebx,ecx,esi,edi .
---------------------------------------------------------------------------------------------
DestroyEvent:
Переносит EVENT в список FreeEvents, чистит поля .magic,.destroy,.pid,.id.
Событие может принадлежать другому потоку.
Принимает:
eax - указатель на событие;
ebx - id, уникальный идентификатор события.
Возвращает:
eax - 0 при ошибке, не 0 при успехе.
Портит: eax,ebx,ecx .
---------------------------------------------------------------------------------------------
WaitEvent:
Бесконечно ожидает установки флага EVENT_SIGNALED в конкретном событии, принадлежащем
вызывающему WaitEvent потоку. Сигнализирующий поток устанавливат этот флаг через
RaiseEvent. Ожидающий поток замораживается путем перевода TASKDATA.state<=TSTATE_WAITING=5.
Перед заморозкой устанавливается флаг EVENT_WATCHED в событии.
Если в полученном событии НЕ установлен MANUAL_RESET, то:
{EVENT_SIGNALED и EVENT_WATCHED по получении события сбрасываются.
При неактивном MANUAL_DESTROY - событие уничтожается штатно (DestroyEvent),
а при активном - перемещается в список ObjList текущего слота.}
Принимает:
eax - указатель на событие;
ebx - id, уникальный идентификатор события.
Возвращает: ?
Портит: eax,ebx,edx,ecx,esi,edi .
---------------------------------------------------------------------------------------------
WaitEventTimeout:
Ожидает с таймаутом установки флага EVENT_SIGNALED в конкретном событии, принадлежащем
вызывающему WaitEventTimeout потоку. Сигнализирующий поток устанавливат этот флаг через
RaiseEvent. Ожидающий поток замораживается путем перевода TASKDATA.state<=TSTATE_WAITING=5.
Перед заморозкой устанавливается флаг EVENT_WATCHED в событии.
Если в полученном событии НЕ установлен MANUAL_RESET, то:
{EVENT_SIGNALED и EVENT_WATCHED по получении события сбрасываются.
При неактивном MANUAL_DESTROY - событие уничтожается штатно (DestroyEvent),
а при активном - перемещается в список ObjList текущего слота.}
Принимает:
eax - указатель на событие;
ebx - id, уникальный идентификатор события.
ecx - время ожидания в тиках системного таймера.
Возвращает:
eax - 0 - таймаут, если событие не активировалось, или
не 0, если было активировано.
Портит: eax,ebx,edx,ecx,esi,edi .
---------------------------------------------------------------------------------------------
GetEvent:
Бесконечно ожидает любое событие в очереди событий текущего потока. Поток замораживается
путем перевода TASKDATA.state<=TSTATE_WAITING=5. Данные события (EVENT.code+5*dword)
по получении копируются в указанный буфер. Сбрасывает байт приоритета (см. выше) в буфере.
Если в полученном событии НЕ установлен MANUAL_RESET, то:
{EVENT_SIGNALED и EVENT_WATCHED по получении события сбрасываются.
При неактивном MANUAL_DESTROY - событие уничтожается штатно (DestroyEvent),
а при активном - перемещается в список ObjList текущего слота.}
Принимает:
edi - указатель на буфер, куда копировать данные.
Возвращает:
буфер, содержащий следующую информацию:
+0: (EVENT.code) dword: идентификатор последующих данных сигнала
+4: (EVENT.data, поле формально не определено) данные принятого
сигнала (5*dword), формат которых определяется первым dword-ом.
Портит: eax,ebx,edx,ecx,esi,edi .
--------------------------------------------------------------------------------------------
Ф 68.14 для приложений: ;это тот же GetEvent, но с обёрткой.
Бесконечно ожидает любое событие в очереди событий текущего потока. Ожидающий поток
замораживается путем перевода TASKDATA.state<=TSTATE_WAITING=5. Данные события (EVENT.code+5*dword)
копируются в указанный буфер. Сбрасывает байт приоритета (см. выше) в буфере.
Принимает:
eax - 68 - номер функции
ebx - 14 - номер подфункции
ecx - указатель на буфер для информации (размер 6*dword)
Возвращает:
буфер, на который указывает ecx, содержит следующую информацию:
+0: (EVENT.code) dword: идентификатор последующих данных сигнала
+4: (EVENT.data, поле формально не определено) данные принятого
сигнала (5*dword), формат которых определяется первым dword-ом.
Портит:
eax .
---------------------------------------------------------------------------------------------
/kernel/trunk/docs/usbapi_ru.txt
0,0 → 1,249
Когда ядро ​​обнаруживает подключенное устройство USB, оно настраивает его
согласно USB-протокола - SET_ADDRESS + SET_CONFIGURATION. Всегда
устанавливается первая конфигурация. Ядро также читает дескриптор
устройства, чтобы показать некоторую информацию, читает и анализирует
дескриптор конфигурации. Для каждого интерфейса ядро будет искать класс этого
интерфейса и попытается загрузить соответствующий драйвер COFF. В настоящее
время соответствие кодов классов и имен драйверов жестко прописано в коде ядра
и выглядит следующим образом:
3 = usbhid.obj,
7 = usbprint.obj,
8 = usbstor.obj,
9 = поддерживаются самим ядром,
другие = usbother.obj.
 
Драйвер должен быть стандартным драйвером в формате COFF, экспортирующим
процедуру под названием "START" и переменную "version". Загрузчик вызывает
процедуру "START" как STDCALL с одним параметром DRV_ENTRY = 1. При завершении
работы системы, если инициализация драйвера была успешна, "START" процедуру
также вызывает код остановки системы с одним параметром DRV_EXIT = -1.
 
Драйвер должен зарегистрировать себя в качестве драйвера USB в процедуре
"START". Это делается путем вызова экспортируемой ядром функции RegUSBDriver и
возврата её результата в качестве результата "START" процедуры.
 
void* __stdcall RegUSBDriver(
const char* name,
void* handler,
const USBFUNC* usbfunc
);
 
Параметр 'name' должен совпадать с именем драйвера, например "usbhid" для
usbhid.obj.
 
Параметр 'handler' является необязательным. Если он не NULL, то он должен
указывать на стандартный обработчик IOCTL интерфейса, как в обычном (не-USB)
драйвере.
 
Параметр "Usbfunc" представляет собой указатель на следующую структуру:
 
struc USBFUNC
{
.strucsize dd ? ; размер структуры, включая это поле
.add_device dd ? ; указатель на AddDevice процедуру в драйвере
; (необходимо)
.device_disconnect dd ? ; указатель на DeviceDisconnected процедуру в драйвере
; опционально, может быть NULL
; В будущем могут быть добавлены другие функции
}
 
Драйвер ДОЛЖЕН реализовать функцию:
 
void* __stdcall AddDevice(
void* pipe0,
void* configdescr,
void* interfacedescr
);
 
Параметр "Pipe0" - хэндл контрольного канала для нулевой конечной точки
устройства. Он может быть использован в качестве аргумента для
USBControlTransferAsync (см. далее).
 
Параметр 'configdescr' указывает на дескриптор конфигурации и все связанные с
ним данные, представленные так, как их возвращает запрос GET_DESCRIPTOR.
Полный размер данных содержится в поле Length самого дескриптора.
(см. USB2.0 spec.)
 
Параметр 'interfacedescr' указывает на дескриптор интерфейса инициализируемого
в данный момент. Это указатель на данные находящиеся внутри структуры
"configdescr". (Помним, что структура INTERFACE_DESCRIPTOR, находится внутри
структуры CONFIGURATION_DESCRIPTOR. См. USB2.0 Spec.) Обратите внимание, что
одно устройство может реализовывать много интерфейсов и AddDevice может быть
вызвана несколько раз с одним "configdescr" но разными "interfacedescr".
 
Возвращенное значение NULL показывает, что инициализация не была успешной.
Любое другое значение означает инициализацию устройства. Ядро не делает попыток
как-то интерпретировать это значение. Это может быть, например, указатель на
внутренние данные драйвера в памяти, выделенной с помощью Kmalloc или индексом
в какой-то своей таблице. (Помните, что Kmalloc() НЕ stdcall-функция! Она
портит регистр ebx!)
 
Драйвер МОЖЕТ реализовать функцию:
 
void __stdcall DeviceDisconnected(
void* devicedata
);
 
Если данная функция реализована, то ядро вызывает её, когда устройство
отключено, посылая ей в качестве параметра "devicedata" то, что было возвращено
ему функцией "AddDevice" при старте драйвера.
 
Драйвер может использовать следующие функции экспортируемые ядром:
 
void* __stdcall USBOpenPipe(
void* pipe0,
int endpoint,
int maxpacketsize,
int type,
int interval
);
 
Параметр "Pipe0" - хэндл контрольного канала для нулевой конечной точки
устройства. Используется для идентификации устройства.
 
Параметр "endpoint" номер конечной точки USB. Младшие 4 бита, собственно, номер
точки, а бит 7 имеет следующее значение: 0 - для OUT точки, 1 - для IN точки.
Остальные биты должны быть равны нулю.
 
Параметр "maxpacketsize" устанавливает максимальный размер пакета для канала.
 
Параметр "type" устанавливает тип передачи для конечной точки, как это прописано
в USB спецификации:
 
0 = control,
1 = isochronous (сейчас не поддерживается),
2 = bulk,
3 = interrupt.
 
Параметр "interval" игнорируется для control и bulk передач. Для конечных точек
по прерываниям устанавливает периодичность опроса в миллисекундах.
 
Функция возвращает хэндл канала при успешном его открытии либо NULL при ошибке.
Хэндл канала обращается в NULL когда:
а) канал будет явно закрыт функцией USBClosePipe (см. ниже);
б) была выполнена предоставленная драйвером функция "DeviceDisconnected".
 
void __stdcall USBClosePipe(
void* pipe
);
 
Освобождает все ресурсы, связанные с выбранным каналом. Единственный параметр -
указатель на хэндл, который был возвращен функцией USBOpenPipe при открытии
канала. Когда устройство отключается, все связанные с ним каналы закрываются
ядром; нет необходимости в самостоятельном вызове этой функции.
 
void* __stdcall USBNormalTransferAsync(
void* pipe,
void* buffer,
int size,
void* callback,
void* calldata,
int flags
);
void* __stdcall USBControlTransferAsync(
void* pipe,
void* setup,
void* buffer,
int size,
void* callback,
void* calldata,
int flags
);
 
Первая функция ставит в очередь bulk или interrupt передачу для выбранного
канала. Тип и направление передачи фиксированы для bulk и interrupt типов
конечных точек, как это было выбрано функцией USBOpenPipe.
Вторая функция ставит в очередь control передачу для выбранного канала.
Направление этой передачи определяется битом 7 байта 0 пакета "setup"
(0 - для OUT, 1 - для IN передачи). Эта функция возвращает управление немедленно.
По окончании передачи вызывается функция "callback" заданная как аргумент
USB______TransferAsync.
 
Параметр "pipe" - хэндл, возвращенный функцией USBOpenPipe.
 
Параметр 'setup' функции USBControlTransferAsync указывает на 8-байтный
конфигурационный пакет (см. USB2.0 Spec).
 
Параметр "buffer" - это указатель на буфер. Для IN передач он будет заполнен
принятыми данными. Для OUT передач он должен быть заполнен данными, которые мы
хотим передать. Указатель может быть NULL для пустых передач, либо для передач
control, если дополнительных данных не требуется.
 
Параметр "size" - это размер данных для передачи. Он может быть равен 0 для
пустых передач, либо для передач control, если дополнительных данных не требуется.
 
Параметр "callback" - это указатель на функцию, которая будет вызвана по
окончании передачи.
 
Параметр "calldata" будет передан функции "callback" вызываемой по окончании
передачи. Например, он может быть NULL или указывать на данные устройства или
указывать на данные используемые как дополнительные параметры, передаваемые от
вызывающей USB_____TransferAsync функции в callback функцию.
 
Другие данные, связанные с передачей, могут быть помещены до буфера (по смещению)
или после него. Они могут быть использованы из callback-функции, при необходимости.
 
Параметр "flags" - это битовое поле. Бит 0 игнорируется для OUT передач. Для IN
передач он означает, может ли устройство передать меньше данных (бит=1), чем
определено в "size" или нет (бит=0). Остальные биты не используются и должны
быть равны 0.
 
Возвращаемое функциями значение равно NULL в случае ошибки и не NULL если
передача успешно поставлена в очередь. Если происходит ошибка при передаче, то
callback функция будет об этом оповещена.
 
void __stdcall CallbackFunction(
void* pipe,
int status,
void* buffer,
int length,
void* calldata
);
 
Параметры 'pipe', 'buffer', 'calldata' значат то же, что и для
USB_____TransferAsync.
 
Параметр "length" это счетчик переданных байт. Для control передач он отражает
дополнительные 8 байт этапа SETUP. Т.е. 0 означает ошибку на этапе SETUP, а
"size"+8 успешную передачу.
 
Параметр "status" не равен 0 в случае ошибки:
USB_STATUS_OK = 0 ; без ошибок
USB_STATUS_CRC = 1 ; ошибка контрольной суммы
USB_STATUS_BITSTUFF = 2 ; ошибка инверсии битов (bitstuffing)
USB_STATUS_TOGGLE = 3 ; data toggle mismatch
; (Нарушение последовательности DAT0/DAT1)
USB_STATUS_STALL = 4 ; устройство возвратило STALL статус (остановлено)
USB_STATUS_NORESPONSE = 5 ; устройство не отвечает
USB_STATUS_PIDCHECK = 6 ; ошибка в поле PacketID (PID)
USB_STATUS_WRONGPID = 7 ; неожидаемое PacketID (PID) значение
USB_STATUS_OVERRUN = 8 ; слишком много данных от конечной точки
USB_STATUS_UNDERRUN = 9 ; слишком мало данных от конечной точки
USB_STATUS_BUFOVERRUN = 12 ; переполнение внутреннего буфера контроллера
; возможна только для изохронных передач
USB_STATUS_BUFUNDERRUN = 13 ; опустошение внутреннего буфера контроллера
; возможна только для изохронных передач
USB_STATUS_CLOSED = 16 ; канал закрыт либо через ClosePipe, либо в
; результате отключения устройства
 
Если несколько передач были поставлены в очередь для одного канала, то callback
функции для них будут вызываться в порядке постановки передач в очередь.
Если канал был закрыт ввиду USBClosePipe или отключения устройства, то callback
функции (если очередь передач не пуста) получат USB_STATUS_CLOSED.
Вызов DeviceDisconnected() последует после отработки всех оставшихся в очереди
callback функций.
 
void* __stdcall USBGetParam(void* pipe0, int param);
 
Возвращает указатель на некоторые параметры устройства запомненные ядром при
инициализации первой конфигурации. Не передает ничего устройству по шине.
 
pipe0 - хэндл контрольного канала для нулевой конечной точки устройства.
 
param - выбор возвращаемого параметра:
0 - возвратить указатель на дескриптор устройства;
1 - возвратить указатель на дескриптор конфигурации;
2 - возвратить режим шины устройства:
USB_SPEED_FS = 0 ; full-speed
USB_SPEED_LS = 1 ; low-speed
USB_SPEED_HS = 2 ; high-speed