Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 7 Jan 2013 11:52:01 -0200
From:      "Dr. Rolf Jansen" <rj@cyclaero.com>
To:        freebsd-drivers@freebsd.org
Subject:   Re: How to map device addresses into user space 
Message-ID:  <033249A9-9C77-426F-BCA2-3A22CC4F9391@cyclaero.com>
In-Reply-To: <DFC983B8-91B2-4ED8-89B0-FC4BD4AB2576@cyclaero.com>
References:  <DFC983B8-91B2-4ED8-89B0-FC4BD4AB2576@cyclaero.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Am 03.01.2013 um 14:45 schrieb Dr. Rolf Jansen:

> I am building a loadable kernel module for FreeBSD 9.1-RELEASE x86_64 =
for a PCI Data Acquisition board from National Instruments.
>=20
> I need to map the Base Address Registers into user space memory, in =
order to pass the BAR's to the National Instruments Drivers Development =
Kit (NI-DDK). The DDK is a complex set of C++ classes running in user =
space, that read/write directly from/into the BAR's.
>=20
> The FreeBSD bus_space_* functions are useless in this respect, because =
the DDK isn't designed that way, I need the BAR addresses mapped into =
user space.
>=20
> Having the measurement done by the kernel module is not an option =
either, because it is math intensive, and kernel modules are build =
without SSE and with soft float.
>=20
> I got tiny kernel modules/extensions only providing the mapped =
addresses of the PCI BAR's running together with the Measurement =
Routines using the NI-DDK on Darwin (Mac OS X) and Linux.
>=20
> So, how can I map device addresses into user space on FreeBSD?



Many Thanks to everybody, who responded.

I got it working. I wrote a PCI device driver, which in addition to =
open/close/read/write responds also to ioctl and mmap requests from user =
space. In its pci_attach routine, it assembles a custom address_space =
struct containing the addresses of the two BAR's of my NI PCI-DAQ board =
and two huge allocated memory regions for DMA.

   static struct address_space space;
   ...
   ...
   int              bar0_id,  bar1_id;=20
   struct resource *bar0res, *bar1res;

   bar0_id =3D PCIR_BAR(0);
   bar1_id =3D PCIR_BAR(1);

   // these resources must be teared down in pci_detach
   bar0res =3D bus_alloc_resource_any(dev, SYS_RES_MEMORY, &bar0id, =
RF_ACTIVE);
   bar1res =3D bus_alloc_resource_any(dev, SYS_RES_MEMORY, &bar1id, =
RF_ACTIVE);

   // assemble the address_space struct, i.e.
   // virtual addr., size, and offset of its members
   space.bar[0].virtual =3D rman_get_bushandle(bar0res);
   space.bar[1].virtual =3D rman_get_bushandle(bar1res);
   space.dma[0].virtual =3D malloc(dmaAIBufSize, M_MYDAQ, M_WAITOK);
   space.dma[1].virtual =3D malloc(dmaAOBufSize, M_MYDAQ, M_WAITOK);

   space.bar[0].size =3D rman_get_end(bar0res) - rman_get_start(bar0res) =
+ 1;
   space.bar[1].size =3D rman_get_end(bar1res) - rman_get_start(bar1res) =
+ 1;
   space.dma[0].size =3D dmaAIBufSize;
   space.dma[1].size =3D dmaAOBufSize;

   // this is the crucial part -- The Offsets!
   // care must be taken, that the offsets are not negative,
   // therefore, find out the minimum and take this as the base address
   space.base =3D MIN(MIN(space.bar[0].virt, space.bar[1].virt),
                    MIN(space.dma[0].virt, space.dma[1].virt));
   space.bar[0].offs =3D space.bar[0].virt - space.base;
   space.bar[1].offs =3D space.bar[1].virt - space.base;
   space.dma[0].offs =3D space.dma[0].virt - space.base;
   space.dma[1].offs =3D space.dma[1].virt - space.base;


By a call to the ioctl handler of my PCI device driver, this readily =
assembled address_space struct is transferred from the driver to my user =
space measurement controller, and here it must actually be completed by =
subsequent calls to mmap of my device driver:

   space.bar[0].physical =3D mmap(0, space.bar[0].size, =
PROT_READ|PROT_WRITE, MAP_SHARED, fd, space.bar[0].offset);
   space.bar[1].physical =3D mmap(0, space.bar[1].size, =
PROT_READ|PROT_WRITE, MAP_SHARED, fd, space.bar[1].offset);
   space.dma[0].physical =3D mmap(0, space.dma[0].size, =
PROT_READ|PROT_WRITE, MAP_SHARED, fd, space.dma[0].offset);
   space.dma[1].physical =3D mmap(0, space.dma[1].size, =
PROT_READ|PROT_WRITE, MAP_SHARED, fd, space.dma[1].offset);


Because of the coherent offsets, the mmap handler of my PCI device =
driver can be kept quite simple:

static int mydaq_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t =
*paddr, int nprot, vm_memattr_t *memattr)
{
   *paddr =3D vtophys(space.base + offset);
   return 0;
}


This works for me.

Best regards

Rolf




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?033249A9-9C77-426F-BCA2-3A22CC4F9391>