diff --git a/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml
index 078aea0740..ca94063864 100644
--- a/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml
+++ b/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml
@@ -1,216 +1,372 @@
-
- PCI Devices
+
+ PCI Devices
- This chapter will talk about the FreeBSD mechanisms for
- writing a device driver for a device on a PCI bus.
+ This chapter will talk about the FreeBSD mechanisms for
+ writing a device driver for a device on a PCI bus.
- Probe and Attach
+
+ Probe and Attach
+
+ 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.
- 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.
/*
* Simple KLD to play with the PCI functions.
*
* Murray Stokely
*/
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#include <sys/types.h>
#include <sys/module.h>
#include <sys/systm.h> /* uprintf */
#include <sys/errno.h>
#include <sys/param.h> /* defines used in kernel.h */
#include <sys/kernel.h> /* types used in module initialization */
#include <sys/conf.h> /* cdevsw struct */
#include <sys/uio.h> /* uio struct */
#include <sys/malloc.h>
#include <sys/bus.h> /* structs, prototypes for pci bus stuff */
#include <pci/pcivar.h> /* For get_pci macros! */
/* Function prototypes */
d_open_t mypci_open;
d_close_t mypci_close;
d_read_t mypci_read;
d_write_t mypci_write;
/* Character device entry points */
static struct cdevsw mypci_cdevsw = {
mypci_open,
mypci_close,
mypci_read,
mypci_write,
noioctl,
nopoll,
nommap,
nostrategy,
"mypci",
36, /* reserved for lkms - /usr/src/sys/conf/majors */
nodump,
nopsize,
D_TTY,
-1
};
/* vars */
static dev_t sdev;
/* We're more interested in probe/attach than with
open/close/read/write at this point */
int
mypci_open(dev_t dev, int oflags, int devtype, struct proc *p)
{
int err = 0;
uprintf("Opened device \"mypci\" successfully.\n");
return(err);
}
int
mypci_close(dev_t dev, int fflag, int devtype, struct proc *p)
{
int err=0;
uprintf("Closing device \"mypci.\"\n");
return(err);
}
int
mypci_read(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("mypci read!\n");
return err;
}
int
mypci_write(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("mypci write!\n");
return(err);
}
/* PCI Support Functions */
/*
* Return identification string if this is device is ours.
*/
static int
mypci_probe(device_t dev)
{
uprintf("MyPCI Probe\n"
"Vendor ID : 0x%x\n"
"Device ID : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev));
if (pci_get_vendor(dev) == 0x11c1) {
uprintf("We've got the Winmodem, probe successful!\n");
return 0;
}
return ENXIO;
}
/* Attach function is only called if the probe is successful */
static int
mypci_attach(device_t dev)
{
uprintf("MyPCI Attach for : deviceID : 0x%x\n",pci_get_vendor(dev));
sdev = make_dev(&mypci_cdevsw,
0,
UID_ROOT,
GID_WHEEL,
0600,
"mypci");
uprintf("Mypci device loaded.\n");
return ENXIO;
}
/* Detach device. */
static int
mypci_detach(device_t dev)
{
uprintf("Mypci detach!\n");
return 0;
}
/* Called during system shutdown after sync. */
static int
mypci_shutdown(device_t dev)
{
uprintf("Mypci shutdown!\n");
return 0;
}
/*
* Device suspend routine.
*/
static int
mypci_suspend(device_t dev)
{
uprintf("Mypci suspend!\n");
return 0;
}
/*
* Device resume routine.
*/
static int
mypci_resume(device_t dev)
{
uprintf("Mypci resume!\n");
return 0;
}
static device_method_t mypci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, mypci_probe),
DEVMETHOD(device_attach, mypci_attach),
DEVMETHOD(device_detach, mypci_detach),
DEVMETHOD(device_shutdown, mypci_shutdown),
DEVMETHOD(device_suspend, mypci_suspend),
DEVMETHOD(device_resume, mypci_resume),
{ 0, 0 }
};
static driver_t mypci_driver = {
"mypci",
mypci_methods,
0,
/* sizeof(struct mypci_softc), */
};
static devclass_t mypci_devclass;
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);
- Additional Resources
-
- PCI Special Interest
- Group
- PCI System Architecture, Fourth Edition by
+ Additional Resources
+
+ PCI
+ Special Interest Group
+
+ PCI System Architecture, Fourth Edition by
Tom Shanley, et al.
-
-
-
-
+
+
+
+
+
+
+ Bus Resources
+
+ 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).
+
+
+ Base Address Registers
+
+ To do anything particularly useful with a PCI device you
+ will need to obtain the Base Address
+ Registers (BARs) from the PCI Configuration space.
+ The PCI-specific details of obtaining the BAR is abstracted in
+ the bus_alloc_resource() function.
+
+ For example, a typical driver might have something similar
+ to this in the attach() function. :
+
+ 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);
+
+
+
+ Handles for each base address register are kept in the
+ softc structure so that they can be
+ used to write to the device later.
+
+ These handles can then be used to read or write from the
+ device registers with the bus_space_*
+ functions. For example, a driver might contain a shorthand
+ function to read from a board specific register like this :
+
+
+uint16_t
+board_read(struct ni_softc *sc, uint16_t address) {
+ return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
+}
+
+
+ Similarly, one could write to the registers with :
+
+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);
+}
+
+
+ These functions exist in 8bit, 16bit, and 32bit versions
+ and you should use
+ bus_space_{read|write}_{1|2|4}
+ accordingly.
+
+
+
+ Interrupts
+
+ 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.
+
+ Again, a sample from a device
+ attach() function says more than
+ words.
+
+/* 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);
+
+
+
+
+
+ DMA
+ 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,
+ vtophys() to help.
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#define vtophys(virtual_address) (...)
+
+
+ The solution is a bit different on the alpha however, and
+ what we really want is a function called
+ vtobus().
+
+#if defined(__alpha__)
+#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
+#else
+#define vtobus(va) vtophys(va)
+#endif
+
+
+
+
+
+ Deallocating Resources
+
+ It's very important to deallocate all of the resources
+ that were allocated during attach().
+ 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.
+
+
+
+
+
diff --git a/en_US.ISO8859-1/books/developers-handbook/pci/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/pci/chapter.sgml
index 078aea0740..ca94063864 100644
--- a/en_US.ISO8859-1/books/developers-handbook/pci/chapter.sgml
+++ b/en_US.ISO8859-1/books/developers-handbook/pci/chapter.sgml
@@ -1,216 +1,372 @@
-
- PCI Devices
+
+ PCI Devices
- This chapter will talk about the FreeBSD mechanisms for
- writing a device driver for a device on a PCI bus.
+ This chapter will talk about the FreeBSD mechanisms for
+ writing a device driver for a device on a PCI bus.
- Probe and Attach
+
+ Probe and Attach
+
+ 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.
- 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.
/*
* Simple KLD to play with the PCI functions.
*
* Murray Stokely
*/
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#include <sys/types.h>
#include <sys/module.h>
#include <sys/systm.h> /* uprintf */
#include <sys/errno.h>
#include <sys/param.h> /* defines used in kernel.h */
#include <sys/kernel.h> /* types used in module initialization */
#include <sys/conf.h> /* cdevsw struct */
#include <sys/uio.h> /* uio struct */
#include <sys/malloc.h>
#include <sys/bus.h> /* structs, prototypes for pci bus stuff */
#include <pci/pcivar.h> /* For get_pci macros! */
/* Function prototypes */
d_open_t mypci_open;
d_close_t mypci_close;
d_read_t mypci_read;
d_write_t mypci_write;
/* Character device entry points */
static struct cdevsw mypci_cdevsw = {
mypci_open,
mypci_close,
mypci_read,
mypci_write,
noioctl,
nopoll,
nommap,
nostrategy,
"mypci",
36, /* reserved for lkms - /usr/src/sys/conf/majors */
nodump,
nopsize,
D_TTY,
-1
};
/* vars */
static dev_t sdev;
/* We're more interested in probe/attach than with
open/close/read/write at this point */
int
mypci_open(dev_t dev, int oflags, int devtype, struct proc *p)
{
int err = 0;
uprintf("Opened device \"mypci\" successfully.\n");
return(err);
}
int
mypci_close(dev_t dev, int fflag, int devtype, struct proc *p)
{
int err=0;
uprintf("Closing device \"mypci.\"\n");
return(err);
}
int
mypci_read(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("mypci read!\n");
return err;
}
int
mypci_write(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("mypci write!\n");
return(err);
}
/* PCI Support Functions */
/*
* Return identification string if this is device is ours.
*/
static int
mypci_probe(device_t dev)
{
uprintf("MyPCI Probe\n"
"Vendor ID : 0x%x\n"
"Device ID : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev));
if (pci_get_vendor(dev) == 0x11c1) {
uprintf("We've got the Winmodem, probe successful!\n");
return 0;
}
return ENXIO;
}
/* Attach function is only called if the probe is successful */
static int
mypci_attach(device_t dev)
{
uprintf("MyPCI Attach for : deviceID : 0x%x\n",pci_get_vendor(dev));
sdev = make_dev(&mypci_cdevsw,
0,
UID_ROOT,
GID_WHEEL,
0600,
"mypci");
uprintf("Mypci device loaded.\n");
return ENXIO;
}
/* Detach device. */
static int
mypci_detach(device_t dev)
{
uprintf("Mypci detach!\n");
return 0;
}
/* Called during system shutdown after sync. */
static int
mypci_shutdown(device_t dev)
{
uprintf("Mypci shutdown!\n");
return 0;
}
/*
* Device suspend routine.
*/
static int
mypci_suspend(device_t dev)
{
uprintf("Mypci suspend!\n");
return 0;
}
/*
* Device resume routine.
*/
static int
mypci_resume(device_t dev)
{
uprintf("Mypci resume!\n");
return 0;
}
static device_method_t mypci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, mypci_probe),
DEVMETHOD(device_attach, mypci_attach),
DEVMETHOD(device_detach, mypci_detach),
DEVMETHOD(device_shutdown, mypci_shutdown),
DEVMETHOD(device_suspend, mypci_suspend),
DEVMETHOD(device_resume, mypci_resume),
{ 0, 0 }
};
static driver_t mypci_driver = {
"mypci",
mypci_methods,
0,
/* sizeof(struct mypci_softc), */
};
static devclass_t mypci_devclass;
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);
- Additional Resources
-
- PCI Special Interest
- Group
- PCI System Architecture, Fourth Edition by
+ Additional Resources
+
+ PCI
+ Special Interest Group
+
+ PCI System Architecture, Fourth Edition by
Tom Shanley, et al.
-
-
-
-
+
+
+
+
+
+
+ Bus Resources
+
+ 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).
+
+
+ Base Address Registers
+
+ To do anything particularly useful with a PCI device you
+ will need to obtain the Base Address
+ Registers (BARs) from the PCI Configuration space.
+ The PCI-specific details of obtaining the BAR is abstracted in
+ the bus_alloc_resource() function.
+
+ For example, a typical driver might have something similar
+ to this in the attach() function. :
+
+ 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);
+
+
+
+ Handles for each base address register are kept in the
+ softc structure so that they can be
+ used to write to the device later.
+
+ These handles can then be used to read or write from the
+ device registers with the bus_space_*
+ functions. For example, a driver might contain a shorthand
+ function to read from a board specific register like this :
+
+
+uint16_t
+board_read(struct ni_softc *sc, uint16_t address) {
+ return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
+}
+
+
+ Similarly, one could write to the registers with :
+
+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);
+}
+
+
+ These functions exist in 8bit, 16bit, and 32bit versions
+ and you should use
+ bus_space_{read|write}_{1|2|4}
+ accordingly.
+
+
+
+ Interrupts
+
+ 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.
+
+ Again, a sample from a device
+ attach() function says more than
+ words.
+
+/* 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);
+
+
+
+
+
+ DMA
+ 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,
+ vtophys() to help.
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#define vtophys(virtual_address) (...)
+
+
+ The solution is a bit different on the alpha however, and
+ what we really want is a function called
+ vtobus().
+
+#if defined(__alpha__)
+#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
+#else
+#define vtobus(va) vtophys(va)
+#endif
+
+
+
+
+
+ Deallocating Resources
+
+ It's very important to deallocate all of the resources
+ that were allocated during attach().
+ 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.
+
+
+
+
+
diff --git a/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml b/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml
index 078aea0740..ca94063864 100644
--- a/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml
+++ b/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml
@@ -1,216 +1,372 @@
-
- PCI Devices
+
+ PCI Devices
- This chapter will talk about the FreeBSD mechanisms for
- writing a device driver for a device on a PCI bus.
+ This chapter will talk about the FreeBSD mechanisms for
+ writing a device driver for a device on a PCI bus.
- Probe and Attach
+
+ Probe and Attach
+
+ 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.
- 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.
/*
* Simple KLD to play with the PCI functions.
*
* Murray Stokely
*/
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#include <sys/types.h>
#include <sys/module.h>
#include <sys/systm.h> /* uprintf */
#include <sys/errno.h>
#include <sys/param.h> /* defines used in kernel.h */
#include <sys/kernel.h> /* types used in module initialization */
#include <sys/conf.h> /* cdevsw struct */
#include <sys/uio.h> /* uio struct */
#include <sys/malloc.h>
#include <sys/bus.h> /* structs, prototypes for pci bus stuff */
#include <pci/pcivar.h> /* For get_pci macros! */
/* Function prototypes */
d_open_t mypci_open;
d_close_t mypci_close;
d_read_t mypci_read;
d_write_t mypci_write;
/* Character device entry points */
static struct cdevsw mypci_cdevsw = {
mypci_open,
mypci_close,
mypci_read,
mypci_write,
noioctl,
nopoll,
nommap,
nostrategy,
"mypci",
36, /* reserved for lkms - /usr/src/sys/conf/majors */
nodump,
nopsize,
D_TTY,
-1
};
/* vars */
static dev_t sdev;
/* We're more interested in probe/attach than with
open/close/read/write at this point */
int
mypci_open(dev_t dev, int oflags, int devtype, struct proc *p)
{
int err = 0;
uprintf("Opened device \"mypci\" successfully.\n");
return(err);
}
int
mypci_close(dev_t dev, int fflag, int devtype, struct proc *p)
{
int err=0;
uprintf("Closing device \"mypci.\"\n");
return(err);
}
int
mypci_read(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("mypci read!\n");
return err;
}
int
mypci_write(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("mypci write!\n");
return(err);
}
/* PCI Support Functions */
/*
* Return identification string if this is device is ours.
*/
static int
mypci_probe(device_t dev)
{
uprintf("MyPCI Probe\n"
"Vendor ID : 0x%x\n"
"Device ID : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev));
if (pci_get_vendor(dev) == 0x11c1) {
uprintf("We've got the Winmodem, probe successful!\n");
return 0;
}
return ENXIO;
}
/* Attach function is only called if the probe is successful */
static int
mypci_attach(device_t dev)
{
uprintf("MyPCI Attach for : deviceID : 0x%x\n",pci_get_vendor(dev));
sdev = make_dev(&mypci_cdevsw,
0,
UID_ROOT,
GID_WHEEL,
0600,
"mypci");
uprintf("Mypci device loaded.\n");
return ENXIO;
}
/* Detach device. */
static int
mypci_detach(device_t dev)
{
uprintf("Mypci detach!\n");
return 0;
}
/* Called during system shutdown after sync. */
static int
mypci_shutdown(device_t dev)
{
uprintf("Mypci shutdown!\n");
return 0;
}
/*
* Device suspend routine.
*/
static int
mypci_suspend(device_t dev)
{
uprintf("Mypci suspend!\n");
return 0;
}
/*
* Device resume routine.
*/
static int
mypci_resume(device_t dev)
{
uprintf("Mypci resume!\n");
return 0;
}
static device_method_t mypci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, mypci_probe),
DEVMETHOD(device_attach, mypci_attach),
DEVMETHOD(device_detach, mypci_detach),
DEVMETHOD(device_shutdown, mypci_shutdown),
DEVMETHOD(device_suspend, mypci_suspend),
DEVMETHOD(device_resume, mypci_resume),
{ 0, 0 }
};
static driver_t mypci_driver = {
"mypci",
mypci_methods,
0,
/* sizeof(struct mypci_softc), */
};
static devclass_t mypci_devclass;
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);
- Additional Resources
-
- PCI Special Interest
- Group
- PCI System Architecture, Fourth Edition by
+ Additional Resources
+
+ PCI
+ Special Interest Group
+
+ PCI System Architecture, Fourth Edition by
Tom Shanley, et al.
-
-
-
-
+
+
+
+
+
+
+ Bus Resources
+
+ 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).
+
+
+ Base Address Registers
+
+ To do anything particularly useful with a PCI device you
+ will need to obtain the Base Address
+ Registers (BARs) from the PCI Configuration space.
+ The PCI-specific details of obtaining the BAR is abstracted in
+ the bus_alloc_resource() function.
+
+ For example, a typical driver might have something similar
+ to this in the attach() function. :
+
+ 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);
+
+
+
+ Handles for each base address register are kept in the
+ softc structure so that they can be
+ used to write to the device later.
+
+ These handles can then be used to read or write from the
+ device registers with the bus_space_*
+ functions. For example, a driver might contain a shorthand
+ function to read from a board specific register like this :
+
+
+uint16_t
+board_read(struct ni_softc *sc, uint16_t address) {
+ return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
+}
+
+
+ Similarly, one could write to the registers with :
+
+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);
+}
+
+
+ These functions exist in 8bit, 16bit, and 32bit versions
+ and you should use
+ bus_space_{read|write}_{1|2|4}
+ accordingly.
+
+
+
+ Interrupts
+
+ 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.
+
+ Again, a sample from a device
+ attach() function says more than
+ words.
+
+/* 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);
+
+
+
+
+
+ DMA
+ 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,
+ vtophys() to help.
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#define vtophys(virtual_address) (...)
+
+
+ The solution is a bit different on the alpha however, and
+ what we really want is a function called
+ vtobus().
+
+#if defined(__alpha__)
+#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
+#else
+#define vtobus(va) vtophys(va)
+#endif
+
+
+
+
+
+ Deallocating Resources
+
+ It's very important to deallocate all of the resources
+ that were allocated during attach().
+ 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.
+
+
+
+
+