---
authors:
  - 
    author: 'Jeroen Ruigrok van der Werven (asmodai)'
    email: asmodai@FreeBSD.org
  - 
    author: 'Hiten Pandya'
    email: hiten@uk.FreeBSD.org
description: Newbus
next: books/arch-handbook/sound
params:
  path: /books/arch-handbook/newbus/
prev: books/arch-handbook/usb
showBookMenu: 'true'
tags: ["Newbus", "overview", "API"]
title: 'Глава 14. Newbus'
weight: 16
---

[[newbus]]
= Newbus
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 14
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/arch-handbook/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

_Особая благодарность Мэтью Н. Додду, Уорнеру Лошу, Биллу Полу, Дагу Рэбсону, Майку Смиту, Питеру Вемму и Скотту Лонгу_.

Эта глава подробно объясняет фреймворк устройств Newbus.

[[newbus-devdrivers]]
== Драйверы устройств

=== Назначение драйвера устройства

Драйвер устройства — это программный компонент, который предоставляет интерфейс между обобщённым представлением периферийного устройства (например, диска, сетевого адаптера) в ядре и его фактической реализацией. _Интерфейс драйвера устройства (DDI)_ — это определённый интерфейс между ядром и компонентом драйвера устройства.

=== Типы драйверов устройств

В UNIX(R), а следовательно, и в FreeBSD, были времена, когда определялось четыре типа устройств:

* драйверы блочных устройств
* драйверы символьных устройств
* драйверы сетевых устройств
* драйверы псевдоустройств

_Блочные устройства_ работали таким образом, что использовали блоки [данных] фиксированного размера. Этот тип драйвера зависел от так называемого _буферного кэша_, который кэшировал доступные блоки данных в выделенной части памяти. Часто этот буферный кэш был основан на отложенной записи (write-behind), что означало, что при изменении данных в памяти они синхронизировались с диском во время периодической очистки диска системой, тем самым оптимизируя запись.

=== Символьные устройства

Однако в версиях FreeBSD 4.0 и выше различие между блочными и символьными устройствами перестало существовать.

[[newbus-overview]]
== Обзор 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 мост_, который представлен устройством _[.filename]#pcibN#_ на родительской PCI-шине и имеет дочерний элемент _[.filename]#pciN#_ для подключенной шины. Такая структура упрощает реализацию дерева PCI-шин, позволяя использовать общий код как для верхнеуровневых, так и для соединенных через мост шин.

Каждое устройство в архитектуре Newbus запрашивает у своего родителя отображение своих ресурсов. Затем родитель запрашивает у своего собственного родителя, пока запрос не достигнет nexus. Таким образом, по сути, nexus - это единственная часть системы Newbus, которая знает обо всех ресурсах.

[TIP]
====
Устройство ISA может захотеть отобразить свой порт ввода-вывода по адресу `0x230`, поэтому оно запрашивает у своего родителя, в данном случае — шины ISA. Шина ISA передаёт запрос мосту PCI-to-ISA, который, в свою очередь, запрашивает шину PCI. Запрос доходит до моста host-to-PCI и, наконец, до nexus. Прелесть этого восходящего перехода в том, что есть возможность преобразовывать запросы. Например, запрос порта ввода-вывода `0x230` может быть преобразован в отображение памяти по адресу `0xb0000230` на системе MIPS с помощью моста PCI.
====

Распределение ресурсов может контролироваться в любом месте дерева устройств. Например, на многих платформах 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 также позволяет определять методы интерфейса в файлах, предназначенных для этой цели. Это файлы с расширением [.filename]#.m#, которые находятся в иерархии [.filename]#src/sys#.

Ядро системы Newbus представляет собой расширяемую модель «объектно-ориентированного программирования». Каждое устройство в системе имеет таблицу поддерживаемых методов. Система и другие устройства используют эти методы для управления устройством и запроса услуг. Различные методы, поддерживаемые устройством, определяются рядом «интерфейсов». «Интерфейс» — это просто группа связанных методов, которые могут быть реализованы устройством.

В системе Newbus методы для устройства предоставляются различными драйверами устройств в системе. Когда устройство подключается к драйверу во время _автоконфигурации_, оно использует таблицу методов, объявленную драйвером. Устройство может позже _отключиться_ от своего драйвера и _подключиться_ к новому драйверу с новой таблицей методов. Это позволяет динамически заменять драйверы, что может быть полезно для разработки драйверов.

Интерфейсы описываются языком определения интерфейсов, похожим на язык, используемый для определения операций vnode для файловых систем. Интерфейс хранится в файле методов (который обычно называется [.filename]#foo_if.m#).

.Методы Newbus
[example]
====
[.programlisting]
....
      # 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;
....
====

Когда этот интерфейс компилируется, он генерирует заголовочный файл "[.filename]#foo_if.h#", который содержит объявления функций:

[.programlisting]
....
      int FOO_DOIT(device_t dev);
      int FOO_DOIT_TO_CHILD(device_t dev, device_t child);
....

Исходный файл `[.filename]#foo_if.c#` также создается для сопровождения автоматически сгенерированного заголовочного файла; он содержит реализации функций, которые ищут расположение соответствующих функций в таблице методов объекта и вызывают эту функцию.

Система определяет два основных интерфейса. Первый фундаментальный интерфейс называется _"device"_ (устройство) и включает методы, которые относятся ко всем устройствам. Методы в интерфейсе _"device"_ включают _"probe"_ (обнаружение), _"attach"_ (присоединение) и _"detach"_ (отсоединение) для управления обнаружением оборудования, а также _"shutdown"_ (выключение), _"suspend"_ (приостановка) и _"resume"_ (возобновление) для уведомления о критических событиях.

Второй, более сложный интерфейс — _"bus"_. Этот интерфейс содержит методы, подходящие для устройств, имеющих дочерние элементы, включая методы доступа к специфичной для шины информации об устройстве footnote:[man:bus_generic_read_ivar[9] и man:bus_generic_write_ivar[9]], уведомления о событиях (`_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.

[[newbus-api]]
== Newbus API

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

=== Важные места в иерархии исходного кода

[.filename]#src/sys/[arch]/[arch]# - Код ядра для конкретной аппаратной архитектуры находится в этом каталоге. Например, архитектура `i386` или архитектура `SPARC64`.

[.filename]#src/sys/dev/[bus]# - поддержка устройств для конкретной `[bus]` находится в этом каталоге.

[.filename]#src/sys/dev/pci# - Код поддержки шины PCI находится в этом каталоге.

[.filename]#src/sys/[isa|pci]# - В этом каталоге находятся драйверы устройств PCI/ISA. Код поддержки шины PCI/ISA располагался в этом каталоге в FreeBSD версии `4.0`.

=== Важные структуры и определения типов

`devclass_t` - Это определение типа указателя на `struct devclass`.

`device_method_t` - Это то же самое, что и `kobj_method_t` (см. [.filename]#src/sys/kobj.h#).

`device_t` - Это определение типа указателя на структуру `struct device`. `device_t` представляет устройство в системе. Это объект ядра. Подробности реализации см. в [.filename]#src/sys/sys/bus_private.h#.

`driver_t` - Это определение типа, которое ссылается на `struct driver`. Структура `driver` является классом объекта ядра `device`; она также содержит данные, приватные для драйвера.

* Реализация _driver_t_*
[.programlisting]
....
	  struct driver {
		KOBJ_CLASS_FIELDS;
		void	*priv;			/* driver private data */
	  };
....

Тип `device_state_t`, который является перечислением, `device_state`. Он содержит возможные состояния устройства Newbus до и после процесса автонастройки.

*Состояния устройств _device_state_t*
[.programlisting]
....
	  /*
	   * 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;
....
