# Foo subsystem/driver (a comment...) INTERFACE foo METHOD int doit { device_t dev; }; # DEFAULT is the method that will be used, if a method was not # provided via: DEVMETHOD() METHOD void doit_to_child { device_t dev; driver_t child; } DEFAULT doit_generic_to_child;
Глава 14. Newbus
Этот перевод может быть устаревшим. Для того, чтобы помочь с переводом, пожалуйста, обратитесь к Сервер переводов FreeBSD.
Содержание
Особая благодарность Мэтью Н. Додду, Уорнеру Лошу, Биллу Полу, Дагу Рэбсону, Майку Смиту, Питеру Вемму и Скотту Лонгу.
Эта глава подробно объясняет фреймворк устройств Newbus.
14.1. Драйверы устройств
14.1.1. Назначение драйвера устройства
Драйвер устройства — это программный компонент, который предоставляет интерфейс между обобщённым представлением периферийного устройства (например, диска, сетевого адаптера) в ядре и его фактической реализацией. Интерфейс драйвера устройства (DDI) — это определённый интерфейс между ядром и компонентом драйвера устройства.
14.1.2. Типы драйверов устройств
В UNIX®, а следовательно, и в FreeBSD, были времена, когда определялось четыре типа устройств:
драйверы блочных устройств
драйверы символьных устройств
драйверы сетевых устройств
драйверы псевдоустройств
Блочные устройства работали таким образом, что использовали блоки [данных] фиксированного размера. Этот тип драйвера зависел от так называемого буферного кэша, который кэшировал доступные блоки данных в выделенной части памяти. Часто этот буферный кэш был основан на отложенной записи (write-behind), что означало, что при изменении данных в памяти они синхронизировались с диском во время периодической очистки диска системой, тем самым оптимизируя запись.
14.2. Обзор Newbus
Newbus — это реализация новой архитектуры шины, основанной на уровнях абстракции, которая была впервые представлена в FreeBSD 3.0, когда порт для Alpha был добавлен в дерево исходного кода. Однако только в версии 4.0 она стала системой по умолчанию для использования с драйверами устройств. Её цель — предоставить более объектно-ориентированный способ взаимодействия между различными шинами и устройствами, которые хост-система предоставляет операционной системе.
Основные функции включают, среди прочего:
динамическое присоединение
простая модуляризация драйверов
псевдо-шины
Одним из наиболее заметных изменений является переход от плоской и нерегламентированной системы к структуре дерева устройств.
На верхнем уровне находится устройство "root", которое является родительским для всех остальных устройств. Для каждой архитектуры обычно существует единственный дочерний элемент "root", к которому подключены такие компоненты, как мосты host-to-PCI и т.д. Для x86 этим устройством "root" является устройство "nexus". Для Alpha различные модели Alpha имеют разные устройства верхнего уровня, соответствующие различным аппаратным наборам микросхем, включая lca, apecs, cia и tsunami.
Устройство в контексте Newbus представляет собой отдельную аппаратную сущность в системе. Например, каждое PCI-устройство представлено устройством Newbus. Любое устройство в системе может иметь дочерние устройства; устройство, у которого есть дочерние устройства, часто называют "шиной". Примерами распространённых шин в системе являются ISA и PCI, которые управляют списками устройств, подключённых к шинам ISA и PCI соответственно.
Часто соединение между различными типами шин представлено устройством "мост", которое обычно имеет один дочерний элемент для подключенной шины. Примером этого является PCI-to-PCI мост, который представлен устройством pcibN на родительской PCI-шине и имеет дочерний элемент pciN для подключенной шины. Такая структура упрощает реализацию дерева PCI-шин, позволяя использовать общий код как для верхнеуровневых, так и для соединенных через мост шин.
Каждое устройство в архитектуре Newbus запрашивает у своего родителя отображение своих ресурсов. Затем родитель запрашивает у своего собственного родителя, пока запрос не достигнет nexus. Таким образом, по сути, nexus - это единственная часть системы Newbus, которая знает обо всех ресурсах.
Устройство ISA может захотеть отобразить свой порт ввода-вывода по адресу |
Распределение ресурсов может контролироваться в любом месте дерева устройств. Например, на многих платформах Alpha прерывания ISA управляются отдельно от прерываний PCI, а распределение ресурсов для прерываний ISA осуществляется устройством шины ISA Alpha. На IA-32 прерывания ISA и PCI управляются устройством верхнего уровня nexus. Для обеих архитектур управление пространством памяти и портов осуществляется единым объектом — nexus для IA-32 и соответствующим драйвером чипсета на Alpha (например, CIA или tsunami).
Для стандартизации доступа к памяти и ресурсам, отображённым на порты, Newbus интегрирует API bus_space
из NetBSD. Они предоставляют единый API для замены inb/outb и прямых операций чтения/записи в память. Преимущество этого подхода в том, что один драйвер может легко использовать либо регистры, отображённые в память, либо регистры, отображённые на порты (некоторое оборудование поддерживает оба варианта).
Эта поддержка интегрирована в механизм распределения ресурсов. При выделении ресурса драйвер может получить связанные bus_space_tag_t
и bus_space_handle_t
из этого ресурса.
Newbus также позволяет определять методы интерфейса в файлах, предназначенных для этой цели. Это файлы с расширением .m, которые находятся в иерархии src/sys.
Ядро системы Newbus представляет собой расширяемую модель «объектно-ориентированного программирования». Каждое устройство в системе имеет таблицу поддерживаемых методов. Система и другие устройства используют эти методы для управления устройством и запроса услуг. Различные методы, поддерживаемые устройством, определяются рядом «интерфейсов». «Интерфейс» — это просто группа связанных методов, которые могут быть реализованы устройством.
В системе Newbus методы для устройства предоставляются различными драйверами устройств в системе. Когда устройство подключается к драйверу во время автоконфигурации, оно использует таблицу методов, объявленную драйвером. Устройство может позже отключиться от своего драйвера и подключиться к новому драйверу с новой таблицей методов. Это позволяет динамически заменять драйверы, что может быть полезно для разработки драйверов.
Интерфейсы описываются языком определения интерфейсов, похожим на язык, используемый для определения операций vnode для файловых систем. Интерфейс хранится в файле методов (который обычно называется foo_if.m).
Когда этот интерфейс компилируется, он генерирует заголовочный файл "foo_if.h", который содержит объявления функций:
int FOO_DOIT(device_t dev); int FOO_DOIT_TO_CHILD(device_t dev, device_t child);
Исходный файл foo_if.c
также создается для сопровождения автоматически сгенерированного заголовочного файла; он содержит реализации функций, которые ищут расположение соответствующих функций в таблице методов объекта и вызывают эту функцию.
Система определяет два основных интерфейса. Первый фундаментальный интерфейс называется "device" (устройство) и включает методы, которые относятся ко всем устройствам. Методы в интерфейсе "device" включают "probe" (обнаружение), "attach" (присоединение) и "detach" (отсоединение) для управления обнаружением оборудования, а также "shutdown" (выключение), "suspend" (приостановка) и "resume" (возобновление) для уведомления о критических событиях.
Второй, более сложный интерфейс — "bus". Этот интерфейс содержит методы, подходящие для устройств, имеющих дочерние элементы, включая методы доступа к специфичной для шины информации об устройстве [1], уведомления о событиях (child_detached
, driver_added
) и управление ресурсами (alloc_resource
, activate_resource
, deactivate_resource
, release_resource
).
Многие методы в интерфейсе "bus" выполняют сервисы для некоторого дочернего устройства шины. Эти методы обычно используют первые два аргумента для указания шины, предоставляющей сервис, и дочернего устройства, запрашивающего сервис. Для упрощения кода драйвера многие из этих методов имеют вспомогательные функции, которые находят родительское устройство и вызывают метод у родителя. Например, метод BUS_TEARDOWN_INTR(device_t dev, device_t child, …)
может быть вызван с помощью функции bus_teardown_intr(device_t child, …)
.
Некоторые типы шин в системе определяют дополнительные интерфейсы для предоставления доступа к специфичной для шины функциональности. Например, драйвер шины PCI определяет интерфейс "pci", который имеет два метода read_config
и write_config
для доступа к конфигурационным регистрам устройства PCI.
14.3. Newbus API
Поскольку API Newbus очень обширен, в этом разделе предпринята попытка его документирования. Дополнительная информация будет добавлена в следующей версии этого документа.
14.3.1. Важные места в иерархии исходного кода
src/sys/[arch]/[arch] - Код ядра для конкретной аппаратной архитектуры находится в этом каталоге. Например, архитектура i386
или архитектура SPARC64
.
src/sys/dev/[bus] - поддержка устройств для конкретной [bus]
находится в этом каталоге.
src/sys/dev/pci - Код поддержки шины PCI находится в этом каталоге.
src/sys/[isa|pci] - В этом каталоге находятся драйверы устройств PCI/ISA. Код поддержки шины PCI/ISA располагался в этом каталоге в FreeBSD версии 4.0
.
14.3.2. Важные структуры и определения типов
devclass_t
- Это определение типа указателя на struct devclass
.
device_method_t
- Это то же самое, что и kobj_method_t
(см. src/sys/kobj.h).
device_t
- Это определение типа указателя на структуру struct device
. device_t
представляет устройство в системе. Это объект ядра. Подробности реализации см. в src/sys/sys/bus_private.h.
driver_t
- Это определение типа, которое ссылается на struct driver
. Структура driver
является классом объекта ядра device
; она также содержит данные, приватные для драйвера.
driver_t Implementation
struct driver { KOBJ_CLASS_FIELDS; void *priv; /* driver private data */ };
Тип device_state_t
, который является перечислением, device_state
. Он содержит возможные состояния устройства Newbus до и после процесса автонастройки.
Device States _device_state_t
/* * src/sys/sys/bus.h */ typedef enum device_state { DS_NOTPRESENT, /* not probed or probe failed */ DS_ALIVE, /* probe succeeded */ DS_ATTACHED, /* attach method called */ DS_BUSY /* device is open */ } device_state_t;
Изменено: 14 октября 2025 г. by Vladlen Popolitov