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>.
Информация по подписке на эту рассылку находится на сайте проекта перевода.