FreeBSD предоставляет объектно-ориентированный механизм для запроса ресурсов от родительской шины. Почти все устройства будут находиться в качестве ребёнка не важно какой шины (PCI, ISA, USB, SCSI и т.д) и нуждаться в приобретении ресурсов от родительской шины таких, как сегменты памяти, линии прерываний, DMA каналы.
Чтобы сделать что-либо полезное с PCI устройством вам надо получить доступ к базовым адресным регистрам (Base Adress
Registres (BARs) ) области конфигурации PCI. PCI специфичные детали получения BAR
абстрагированы в функции bus_alloc_resource()
.
К примеру, типичный драйвер должен иметь что-то похожее в функции attach()
:
sc->bar0id = PCI_BAR(0); sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar0id), 0, ~0, 1, RF_ACTIVE); if (sc->bar0res == NULL) { printf("Memory allocation of PCI base register 0 failed!\n"); error = ENXIO; goto fail1; } sc->bar1id = PCI_BAR(1); sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar1id), 0, ~0, 1, RF_ACTIVE); if (sc->bar1res == NULL) { printf("Memory allocation of PCI base register 1 failed!\n"); error = ENXIO; goto fail2; } sc->bar0_bt = rman_get_bustag(sc->bar0res); sc->bar0_bh = rman_get_bushandle(sc->bar0res); sc->bar1_bt = rman_get_bustag(sc->bar1res); sc->bar1_bh = rman_get_bushandle(sc->bar1res);
Дескрипторы каждого базового регистра хранятся в структуре softc
и в дальнейшем могут быть использованы для записи в
устройство.
С помощью функций bus_space_*
эти дескрипторы могут быть
использованы для чтения или записи в регистры устройства. Например, в драйвере может быть
реализована функция чтения данных из определенного регистра устройства:
uint16_t board_read(struct ni_softc *sc, uint16_t address) { return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address); }
Похожим способом делается запись в регистры:
void board_write(struct ni_softc *sc, uint16_t address, uint16_t value) { bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value); }
Эти функции существуют в 8, 16, 32 битных версиях. Используйте bus_space_{read|write}_{1|2|4}
соответственно.
Прерывания (IRQ) распределяются с помощью объектно-ориентированного кода шины способом, похожим на способ распределения ресурсов памяти. Сначала IRQ ресурс выделяется от родительской шины, затем настраивается обработчик прерываний для связи с этим IRQ.
Пример функции attach()
скажет больше чем слова.
/* Get the IRQ resource */ sc->irqid = 0x0; sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &(sc->irqid), 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (sc->irqres == NULL) { printf("IRQ allocation failed!\n"); error = ENXIO; goto fail3; } /* Now we should set up the interrupt handler */ error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC, my_handler, sc, &(sc->handler)); if (error) { printf("Couldn't set up irq\n"); goto fail4; } sc->irq_bt = rman_get_bustag(sc->irqres); sc->irq_bh = rman_get_bushandle(sc->irqres);
Осторожно отсоединяйте программу драйвера. Вы должны успокоить поток прерываний
устройства и удалить дескриптор прерывания. После выполнения функции bus_teardown_intr()
ваш дескриптор прерываний не будет больше
вызываться и все треды, которые возможно могли использовать этот дескриптор завершились.
Так как эта функция может "засыпать", то вы не должны удерживать мьютексы при вызове этой
функции.
Это глава является устаревшей и существует только по историческим причинам. Для работы
с данной возможностью используйте семейство функций bus_space_dma*()
. Этот параграф может будет убран при обновлении
этой главы. На данный момент в API вносятся изменения, и когда все вопросы будут решены
было бы неплохо обновить эту главу.
На ПК, периферийные устройства желающие использовать возможности DMA должны работать с
физическими адресами, что является проблемой, так как FreeBSD использует виртуальную
память и почти полностью имеет дело с виртуальными адресами. К счастью, существует
функция vtophys()
способная помочь.
#include <vm/vm.h> #include <vm/pmap.h> #define vtophys(virtual_address) (...)
Немного по другому решается данная проблема на платформе alpha. В этой ситуации
хорошим решением будет использование макроопределения vtobus()
.
#if defined(__alpha__) #define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va) #else #define vtobus(va) vtophys(va) #endif
Очень важно освободить все реcурсы, выделенные с помощью attach()
. Осторожно освобождайте корректные вещи даже на отказных
условиях, чтобы система оставалась в рабочем состоянии, пока ваш драйвер не умрет.
Пред. | Начало | След. |
Устройства PCI | Уровень выше | * Common Access Method SCSI Controllers |
Этот, и другие документы, могут быть скачаны с ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.
По вопросам, связанным с FreeBSD, прочитайте документацию прежде чем писать в <questions@FreeBSD.org>.
По вопросам, связанным с этой документацией, пишите <doc@FreeBSD.org>.
По вопросам, связанным с русским переводом документации, пишите в рассылку <frdp@FreeBSD.org.ua>.
Информация по подписке на эту рассылку находится на сайте проекта перевода.