Add PCI resource allocation section for (BARs, IRQs, DMA)
This commit is contained in:
parent
c19dfc27c0
commit
53f5f0487c
Notes:
svn2git
2020-12-08 03:00:23 +00:00
svn path=/head/; revision=9152
3 changed files with 525 additions and 57 deletions
en_US.ISO8859-1/books
en_US.ISO_8859-1/books/developers-handbook/pci
|
@ -1,20 +1,22 @@
|
|||
<!--
|
||||
The FreeBSD Documentation Project
|
||||
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.1 2000/11/28 19:07:46 asmodai Exp $
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.2 2001/04/09 00:33:42 dd Exp $
|
||||
-->
|
||||
|
||||
<chapter id="pci">
|
||||
<title>PCI Devices</title>
|
||||
<chapter id="pci">
|
||||
<title>PCI Devices</title>
|
||||
|
||||
<para>This chapter will talk about the FreeBSD mechanisms for
|
||||
writing a device driver for a device on a PCI bus.</para>
|
||||
<para>This chapter will talk about the FreeBSD mechanisms for
|
||||
writing a device driver for a device on a PCI bus.</para>
|
||||
|
||||
<sect1><title>Probe and Attach</title>
|
||||
<sect1>
|
||||
<title>Probe and Attach</title>
|
||||
|
||||
<para>Information here about how the PCI bus code iterates through
|
||||
the unattached devices and see if a newly loaded kld will attach
|
||||
to any of them.</para>
|
||||
|
||||
<para>Information here about how the PCI bus code iterates
|
||||
through the unattached devices and see if a newly loaded kld
|
||||
will attach to any of them.</para>
|
||||
<programlisting>/*
|
||||
* Simple KLD to play with the PCI functions.
|
||||
*
|
||||
|
@ -201,16 +203,170 @@ static devclass_t mypci_devclass;
|
|||
|
||||
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
|
||||
|
||||
<para>Additional Resources
|
||||
<itemizedlist>
|
||||
<listitem><simpara><ulink
|
||||
url="http://www.pcisig.org">PCI Special Interest
|
||||
Group</ulink></simpara></listitem>
|
||||
<listitem><simpara>PCI System Architecture, Fourth Edition by
|
||||
<para>Additional Resources
|
||||
<itemizedlist>
|
||||
<listitem><simpara><ulink url="http://www.pcisig.org">PCI
|
||||
Special Interest Group</ulink></simpara></listitem>
|
||||
|
||||
<listitem><simpara>PCI System Architecture, Fourth Edition by
|
||||
Tom Shanley, et al.</simpara></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Bus Resources</title>
|
||||
|
||||
<para>FreeBSD provides an object-oriented mechanism for requesting
|
||||
resources from a parent bus. Almost all devices will be a child
|
||||
member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these
|
||||
devices need to acquire resources from their parent bus (such as
|
||||
memory segments, interrupt lines, or DMA channels).</para>
|
||||
|
||||
<sect2>
|
||||
<title>Base Address Registers</title>
|
||||
|
||||
<para>To do anything particularly useful with a PCI device you
|
||||
will need to obtain the <emphasis>Base Address
|
||||
Registers</emphasis> (BARs) from the PCI Configuration space.
|
||||
The PCI-specific details of obtaining the BAR is abstracted in
|
||||
the <function>bus_alloc_resource()</function> function.</para>
|
||||
|
||||
<para>For example, a typical driver might have something similar
|
||||
to this in the <function>attach()</function> function. : </para>
|
||||
|
||||
<programlisting> sc->bar0id = 0x10;
|
||||
sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar0id),
|
||||
0, ~0, 1, RF_ACTIVE);
|
||||
if (sc->bar0res == NULL) {
|
||||
uprintf("Memory allocation of PCI base register 0 failed!\n");
|
||||
error = ENXIO;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
sc->bar1id = 0x14;
|
||||
sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar1id),
|
||||
0, ~0, 1, RF_ACTIVE);
|
||||
if (sc->bar1res == NULL) {
|
||||
uprintf("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);
|
||||
|
||||
</programlisting>
|
||||
|
||||
<para>Handles for each base address register are kept in the
|
||||
<structname>softc</structname> structure so that they can be
|
||||
used to write to the device later.</para>
|
||||
|
||||
<para>These handles can then be used to read or write from the
|
||||
device registers with the <function>bus_space_*</function>
|
||||
functions. For example, a driver might contain a shorthand
|
||||
function to read from a board specific register like this :
|
||||
</para>
|
||||
|
||||
<programlisting>uint16_t
|
||||
board_read(struct ni_softc *sc, uint16_t address) {
|
||||
return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>Similarly, one could write to the registers with : </para>
|
||||
|
||||
<programlisting>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);
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>These functions exist in 8bit, 16bit, and 32bit versions
|
||||
and you should use
|
||||
<function>bus_space_{read|write}_{1|2|4}</function>
|
||||
accordingly.</para>
|
||||
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Interrupts</title>
|
||||
|
||||
<para>Interrupts are allocated from the object-oriented bus code
|
||||
in a way similar to the memory resources. First an IRQ
|
||||
resource must be allocated from the parent bus, and then the
|
||||
interrupt handler must be setup to deal with this IRQ.</para>
|
||||
|
||||
<para>Again, a sample from a device
|
||||
<function>attach()</function> function says more than
|
||||
words.</para>
|
||||
|
||||
<programlisting>/* 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) {
|
||||
uprintf("IRQ allocation failed!\n");
|
||||
error = ENXIO;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
/* Now we should setup 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);
|
||||
</programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>DMA</title>
|
||||
<para>On the PC, peripherals that want to do bus-mastering DMA
|
||||
must deal with physical addresses. This is a problem since
|
||||
FreeBSD uses virtual memory and deals almost exclusively with
|
||||
virtual addresses. Fortunately, there is a function,
|
||||
<function>vtophys()</function> to help.</para>
|
||||
|
||||
<programlisting>#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#define vtophys(virtual_address) (...)
|
||||
</programlisting>
|
||||
|
||||
<para>The solution is a bit different on the alpha however, and
|
||||
what we really want is a function called
|
||||
<function>vtobus()</function>.</para>
|
||||
|
||||
<programlisting>#if defined(__alpha__)
|
||||
#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
|
||||
#else
|
||||
#define vtobus(va) vtophys(va)
|
||||
#endif
|
||||
</programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Deallocating Resources</title>
|
||||
|
||||
<para>It's very important to deallocate all of the resources
|
||||
that were allocated during <function>attach()</function>.
|
||||
Care must be taken to deallocate the correct stuff even on a
|
||||
failure condition so that the system will remain useable while
|
||||
your driver dies.</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
<!--
|
||||
The FreeBSD Documentation Project
|
||||
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.1 2000/11/28 19:07:46 asmodai Exp $
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.2 2001/04/09 00:33:42 dd Exp $
|
||||
-->
|
||||
|
||||
<chapter id="pci">
|
||||
<title>PCI Devices</title>
|
||||
<chapter id="pci">
|
||||
<title>PCI Devices</title>
|
||||
|
||||
<para>This chapter will talk about the FreeBSD mechanisms for
|
||||
writing a device driver for a device on a PCI bus.</para>
|
||||
<para>This chapter will talk about the FreeBSD mechanisms for
|
||||
writing a device driver for a device on a PCI bus.</para>
|
||||
|
||||
<sect1><title>Probe and Attach</title>
|
||||
<sect1>
|
||||
<title>Probe and Attach</title>
|
||||
|
||||
<para>Information here about how the PCI bus code iterates through
|
||||
the unattached devices and see if a newly loaded kld will attach
|
||||
to any of them.</para>
|
||||
|
||||
<para>Information here about how the PCI bus code iterates
|
||||
through the unattached devices and see if a newly loaded kld
|
||||
will attach to any of them.</para>
|
||||
<programlisting>/*
|
||||
* Simple KLD to play with the PCI functions.
|
||||
*
|
||||
|
@ -201,16 +203,170 @@ static devclass_t mypci_devclass;
|
|||
|
||||
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
|
||||
|
||||
<para>Additional Resources
|
||||
<itemizedlist>
|
||||
<listitem><simpara><ulink
|
||||
url="http://www.pcisig.org">PCI Special Interest
|
||||
Group</ulink></simpara></listitem>
|
||||
<listitem><simpara>PCI System Architecture, Fourth Edition by
|
||||
<para>Additional Resources
|
||||
<itemizedlist>
|
||||
<listitem><simpara><ulink url="http://www.pcisig.org">PCI
|
||||
Special Interest Group</ulink></simpara></listitem>
|
||||
|
||||
<listitem><simpara>PCI System Architecture, Fourth Edition by
|
||||
Tom Shanley, et al.</simpara></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Bus Resources</title>
|
||||
|
||||
<para>FreeBSD provides an object-oriented mechanism for requesting
|
||||
resources from a parent bus. Almost all devices will be a child
|
||||
member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these
|
||||
devices need to acquire resources from their parent bus (such as
|
||||
memory segments, interrupt lines, or DMA channels).</para>
|
||||
|
||||
<sect2>
|
||||
<title>Base Address Registers</title>
|
||||
|
||||
<para>To do anything particularly useful with a PCI device you
|
||||
will need to obtain the <emphasis>Base Address
|
||||
Registers</emphasis> (BARs) from the PCI Configuration space.
|
||||
The PCI-specific details of obtaining the BAR is abstracted in
|
||||
the <function>bus_alloc_resource()</function> function.</para>
|
||||
|
||||
<para>For example, a typical driver might have something similar
|
||||
to this in the <function>attach()</function> function. : </para>
|
||||
|
||||
<programlisting> sc->bar0id = 0x10;
|
||||
sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar0id),
|
||||
0, ~0, 1, RF_ACTIVE);
|
||||
if (sc->bar0res == NULL) {
|
||||
uprintf("Memory allocation of PCI base register 0 failed!\n");
|
||||
error = ENXIO;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
sc->bar1id = 0x14;
|
||||
sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar1id),
|
||||
0, ~0, 1, RF_ACTIVE);
|
||||
if (sc->bar1res == NULL) {
|
||||
uprintf("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);
|
||||
|
||||
</programlisting>
|
||||
|
||||
<para>Handles for each base address register are kept in the
|
||||
<structname>softc</structname> structure so that they can be
|
||||
used to write to the device later.</para>
|
||||
|
||||
<para>These handles can then be used to read or write from the
|
||||
device registers with the <function>bus_space_*</function>
|
||||
functions. For example, a driver might contain a shorthand
|
||||
function to read from a board specific register like this :
|
||||
</para>
|
||||
|
||||
<programlisting>uint16_t
|
||||
board_read(struct ni_softc *sc, uint16_t address) {
|
||||
return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>Similarly, one could write to the registers with : </para>
|
||||
|
||||
<programlisting>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);
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>These functions exist in 8bit, 16bit, and 32bit versions
|
||||
and you should use
|
||||
<function>bus_space_{read|write}_{1|2|4}</function>
|
||||
accordingly.</para>
|
||||
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Interrupts</title>
|
||||
|
||||
<para>Interrupts are allocated from the object-oriented bus code
|
||||
in a way similar to the memory resources. First an IRQ
|
||||
resource must be allocated from the parent bus, and then the
|
||||
interrupt handler must be setup to deal with this IRQ.</para>
|
||||
|
||||
<para>Again, a sample from a device
|
||||
<function>attach()</function> function says more than
|
||||
words.</para>
|
||||
|
||||
<programlisting>/* 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) {
|
||||
uprintf("IRQ allocation failed!\n");
|
||||
error = ENXIO;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
/* Now we should setup 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);
|
||||
</programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>DMA</title>
|
||||
<para>On the PC, peripherals that want to do bus-mastering DMA
|
||||
must deal with physical addresses. This is a problem since
|
||||
FreeBSD uses virtual memory and deals almost exclusively with
|
||||
virtual addresses. Fortunately, there is a function,
|
||||
<function>vtophys()</function> to help.</para>
|
||||
|
||||
<programlisting>#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#define vtophys(virtual_address) (...)
|
||||
</programlisting>
|
||||
|
||||
<para>The solution is a bit different on the alpha however, and
|
||||
what we really want is a function called
|
||||
<function>vtobus()</function>.</para>
|
||||
|
||||
<programlisting>#if defined(__alpha__)
|
||||
#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
|
||||
#else
|
||||
#define vtobus(va) vtophys(va)
|
||||
#endif
|
||||
</programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Deallocating Resources</title>
|
||||
|
||||
<para>It's very important to deallocate all of the resources
|
||||
that were allocated during <function>attach()</function>.
|
||||
Care must be taken to deallocate the correct stuff even on a
|
||||
failure condition so that the system will remain useable while
|
||||
your driver dies.</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
<!--
|
||||
The FreeBSD Documentation Project
|
||||
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.1 2000/11/28 19:07:46 asmodai Exp $
|
||||
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.2 2001/04/09 00:33:42 dd Exp $
|
||||
-->
|
||||
|
||||
<chapter id="pci">
|
||||
<title>PCI Devices</title>
|
||||
<chapter id="pci">
|
||||
<title>PCI Devices</title>
|
||||
|
||||
<para>This chapter will talk about the FreeBSD mechanisms for
|
||||
writing a device driver for a device on a PCI bus.</para>
|
||||
<para>This chapter will talk about the FreeBSD mechanisms for
|
||||
writing a device driver for a device on a PCI bus.</para>
|
||||
|
||||
<sect1><title>Probe and Attach</title>
|
||||
<sect1>
|
||||
<title>Probe and Attach</title>
|
||||
|
||||
<para>Information here about how the PCI bus code iterates through
|
||||
the unattached devices and see if a newly loaded kld will attach
|
||||
to any of them.</para>
|
||||
|
||||
<para>Information here about how the PCI bus code iterates
|
||||
through the unattached devices and see if a newly loaded kld
|
||||
will attach to any of them.</para>
|
||||
<programlisting>/*
|
||||
* Simple KLD to play with the PCI functions.
|
||||
*
|
||||
|
@ -201,16 +203,170 @@ static devclass_t mypci_devclass;
|
|||
|
||||
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
|
||||
|
||||
<para>Additional Resources
|
||||
<itemizedlist>
|
||||
<listitem><simpara><ulink
|
||||
url="http://www.pcisig.org">PCI Special Interest
|
||||
Group</ulink></simpara></listitem>
|
||||
<listitem><simpara>PCI System Architecture, Fourth Edition by
|
||||
<para>Additional Resources
|
||||
<itemizedlist>
|
||||
<listitem><simpara><ulink url="http://www.pcisig.org">PCI
|
||||
Special Interest Group</ulink></simpara></listitem>
|
||||
|
||||
<listitem><simpara>PCI System Architecture, Fourth Edition by
|
||||
Tom Shanley, et al.</simpara></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Bus Resources</title>
|
||||
|
||||
<para>FreeBSD provides an object-oriented mechanism for requesting
|
||||
resources from a parent bus. Almost all devices will be a child
|
||||
member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these
|
||||
devices need to acquire resources from their parent bus (such as
|
||||
memory segments, interrupt lines, or DMA channels).</para>
|
||||
|
||||
<sect2>
|
||||
<title>Base Address Registers</title>
|
||||
|
||||
<para>To do anything particularly useful with a PCI device you
|
||||
will need to obtain the <emphasis>Base Address
|
||||
Registers</emphasis> (BARs) from the PCI Configuration space.
|
||||
The PCI-specific details of obtaining the BAR is abstracted in
|
||||
the <function>bus_alloc_resource()</function> function.</para>
|
||||
|
||||
<para>For example, a typical driver might have something similar
|
||||
to this in the <function>attach()</function> function. : </para>
|
||||
|
||||
<programlisting> sc->bar0id = 0x10;
|
||||
sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar0id),
|
||||
0, ~0, 1, RF_ACTIVE);
|
||||
if (sc->bar0res == NULL) {
|
||||
uprintf("Memory allocation of PCI base register 0 failed!\n");
|
||||
error = ENXIO;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
sc->bar1id = 0x14;
|
||||
sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->bar1id),
|
||||
0, ~0, 1, RF_ACTIVE);
|
||||
if (sc->bar1res == NULL) {
|
||||
uprintf("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);
|
||||
|
||||
</programlisting>
|
||||
|
||||
<para>Handles for each base address register are kept in the
|
||||
<structname>softc</structname> structure so that they can be
|
||||
used to write to the device later.</para>
|
||||
|
||||
<para>These handles can then be used to read or write from the
|
||||
device registers with the <function>bus_space_*</function>
|
||||
functions. For example, a driver might contain a shorthand
|
||||
function to read from a board specific register like this :
|
||||
</para>
|
||||
|
||||
<programlisting>uint16_t
|
||||
board_read(struct ni_softc *sc, uint16_t address) {
|
||||
return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>Similarly, one could write to the registers with : </para>
|
||||
|
||||
<programlisting>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);
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>These functions exist in 8bit, 16bit, and 32bit versions
|
||||
and you should use
|
||||
<function>bus_space_{read|write}_{1|2|4}</function>
|
||||
accordingly.</para>
|
||||
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Interrupts</title>
|
||||
|
||||
<para>Interrupts are allocated from the object-oriented bus code
|
||||
in a way similar to the memory resources. First an IRQ
|
||||
resource must be allocated from the parent bus, and then the
|
||||
interrupt handler must be setup to deal with this IRQ.</para>
|
||||
|
||||
<para>Again, a sample from a device
|
||||
<function>attach()</function> function says more than
|
||||
words.</para>
|
||||
|
||||
<programlisting>/* 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) {
|
||||
uprintf("IRQ allocation failed!\n");
|
||||
error = ENXIO;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
/* Now we should setup 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);
|
||||
</programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>DMA</title>
|
||||
<para>On the PC, peripherals that want to do bus-mastering DMA
|
||||
must deal with physical addresses. This is a problem since
|
||||
FreeBSD uses virtual memory and deals almost exclusively with
|
||||
virtual addresses. Fortunately, there is a function,
|
||||
<function>vtophys()</function> to help.</para>
|
||||
|
||||
<programlisting>#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#define vtophys(virtual_address) (...)
|
||||
</programlisting>
|
||||
|
||||
<para>The solution is a bit different on the alpha however, and
|
||||
what we really want is a function called
|
||||
<function>vtobus()</function>.</para>
|
||||
|
||||
<programlisting>#if defined(__alpha__)
|
||||
#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
|
||||
#else
|
||||
#define vtobus(va) vtophys(va)
|
||||
#endif
|
||||
</programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Deallocating Resources</title>
|
||||
|
||||
<para>It's very important to deallocate all of the resources
|
||||
that were allocated during <function>attach()</function>.
|
||||
Care must be taken to deallocate the correct stuff even on a
|
||||
failure condition so that the system will remain useable while
|
||||
your driver dies.</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue