diff options
Diffstat (limited to 'en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml')
-rw-r--r-- | en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml | 372 |
1 files changed, 0 insertions, 372 deletions
diff --git a/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml deleted file mode 100644 index ca94063864..0000000000 --- a/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml +++ /dev/null @@ -1,372 +0,0 @@ -<!-- - The FreeBSD Documentation Project - - $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> - - <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> - - <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. - * - * 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(<literal>&</literal>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);</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 - Tom Shanley, et al.</simpara></listitem> - - </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> - - |