Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 23 Aug 1998 13:47:51 -0400 (EDT)
From:      Bill Paul <wpaul@skynet.ctr.columbia.edu>
To:        chuckr@glue.umd.edu (Chuck Robey)
Cc:        hackers@FreeBSD.ORG
Subject:   Re: PCI devices
Message-ID:  <199808231747.NAA18976@skynet.ctr.columbia.edu>
In-Reply-To: <Pine.BSF.4.00.9808231030210.361-100000@picnic.mat.net> from "Chuck Robey" at Aug 23, 98 10:32:37 am

next in thread | previous in thread | raw e-mail | index | archive | help
Of all the gin joints in all the towns in all the world, Chuck Robey had 
to walk into mine and say:

> Does anyone know where I might find a good example of probing and
> attaching a pci device?  I'm talking about the C code.  I'm looking at a
> PCI sound card, but I can't find anything on how to do sound stuff using
> PCI.
> 
> Thanks.

I would start by looking at the device drivers in /sys/pci. That's
where I started.

For the most part, you don't need to do very much to probe a PCI device:
the PCI support code finds all PCI devices that are in the system for you.
The PCI support code knows about what drivers are in the system by using
linker sets: each driver has a linker set which specifies the name of
the probe and attach routines in the driver (which are normally declared
static), and the PCI code will run down the list of available drivers
and call the probe routine of each one.

The probe routine is very simple: the PCI support code takes the PCI
device ID (which is a 32-bit value containing a 16-bit vendor ID and
16-bit device ID) for all the PCI devices that it finds and passes them
in turn to all available device drivers. The driver probe routine compares
the ID to a list of vendor/drvice IDs that it knows about, and if the
supplied ID matches one in its list, it returns a pointer to a string
describing the device. If there's no match, it returns NULL.

Once the PCI code gets a positive result back from a probe routine,
it will call back to the attach routine later to complete the setup.
The attach routine is passed a PCI config_id handle which can be used
with various functions to read or write PCI registers from the device.

There are a couple of things you have to do in the attach routine:

- Obtain the iobase or membase address of the device. You need this
  in order to be able to issue commands to the device or read its
  non-PCI registers. PCI devices can be controlled in one of two
  ways: you can either use PIO accesses through I/O space using
  inb/outb and friends, or you can map the device's register directly
  into system memory, in which case you can just read and write directly
  to memory locations. Not all devices support memory mapping however;
  check your manual. Also, sometimes the PCI BIOS will not enable
  memory mapped access when configuring the device: if this is
  the case, you need to use pci_conf_write() to flip on the right
  bit in the command register. There's also a bit for enabling
  iospace access. (You can use pci_conf_read() to read the register.)

- If you choose to use memory mapped access, you need to call
  pci_map_mem() to map the memory region and obtain its base address.
  You will get both a kernel virtual address and a physical one.
  You will most likely want the kernel virtual one.

- Enable bus mastering support. If your device uses bus-master DMA,
  you might have to enable it by setting the correct bit in the
  command register. Again, this should be set by the PCI BIOS, but
  sometimes it isn't.

  Note that when using bus-master DMA, you generally
  have to give the chip the address of a memory buffer from which
  data will be transfered (or into which data will be transfered).
  This address must be a physical address, not a kernel virtual
  address, since the device knows nothing about the kernel's virtual
  memory mappings. You obtain the physical address using vtophys(),
  however once you have the physical address I don't think you can
  transform it back to a kernel virtual address since there may be
  more than one virtual to physical mapping.

  Also note that sometimes bus master PCI devices will exhibit strange
  failure modes if you put them in the wrong slot. My understanding is
  that PCI bus master devices must go in bus master slots, and putting
  them in PCI slave slots won't work.

- You need to install your interrupt handler. This is done using
  pci_map_int(). You need to supply a pointer to your interrupt handler
  routine.

This is generic PCI setup which applies to any PCI device: the offsets
of the command register and IRQ line register are well known and defined
in /sys/pci/pcireg.h, along with other standard register offsets common
to all PCI devices. Your device may have other non-standard PCI
registers; if so, they should be described in the documentation. If
not, LART the vendor.

One newer wrinkle is power management. Devices which support power 
management have a capabilities ID register, which will have a bit
set to indicate that power management is available. There's also a
power management status register and control register. You may need to 
insure that the device is set to the correct power mode (D0 is usually 
the full power mode) before putting the device into operation. Windoze is 
known to set some devices into low power mode during shutdown; if you 
warm boot from Windoze to FreeBSD, you may then have to put the device 
back into the right power mode in the attach routine (the PCI BIOS is 
supposed to do this, but older BIOSes may not).

As for 'PCI sound stuff,' that I don't know about. So far I've only
written network drivers, but I'd wager that there are a couple of
similarities. You probably have to provide a way to transfer large
streams of encoded audio data into and out of the adapter, which is
probably where DMA comes in. You have to give the card pointers
to buffers containing audio data to be played, then wait for an
interrupt from the card telling you the transfer is complete so you
can free the buffers and transfer more. And you need to be able to
program mixer levels and things using internal registers. I don't know 
about things like MIDI or wave tables so I can't imagine how those work.
I'm assuming you do have the proper documentation for your board;
if not you're going to be totally lost.

-Bill

P.S.: Don't be fooled by marketing literature (or clueless lusers) who
      talk about PCI adapters with "plug & play" support. PCI implies
      "plug & play" in that the PCI BIOS works out what iospace and
      membase addresses and IRQs to assign to cards in order to avoid
      resource conflicts. But this is not the same as Plug & Play (tm),
      which I think only applies to ISA boards. I sometimes see people
      on mailing lists or newsgroups complaining that their PCI devices
      don't work for some reason, and often someone follows up with
      a suggestion to "disable plug & play| support. These people are
      confused: PCI devices don't support Plug & Play (tm), so there's
      nothing to turn off.

-- 
=============================================================================
-Bill Paul            (212) 854-6020 | System Manager, Master of Unix-Fu
Work:         wpaul@ctr.columbia.edu | Center for Telecommunications Research
Home:  wpaul@skynet.ctr.columbia.edu | Columbia University, New York City
=============================================================================
 "It is not I who am crazy; it is I who am mad!" - Ren Hoek, "Space Madness"
=============================================================================

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199808231747.NAA18976>