From owner-freebsd-drivers@FreeBSD.ORG Mon Jan 7 13:52:10 2013 Return-Path: Delivered-To: freebsd-drivers@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 8DE7E163 for ; Mon, 7 Jan 2013 13:52:10 +0000 (UTC) (envelope-from rj@cyclaero.com) Received: from mo6-p00-ob.rzone.de (mo6-p00-ob.rzone.de [IPv6:2a01:238:20a:202:5300::1]) by mx1.freebsd.org (Postfix) with ESMTP id 27043AE0 for ; Mon, 7 Jan 2013 13:52:09 +0000 (UTC) X-RZG-AUTH: :O2kGeEG7b/pS1E6gSHOyjPKyNsg/5l1He+DgCy9/8FSej6CwUysqfN3Dd+qW8pifIOtG X-RZG-CLASS-ID: mo00 Received: from rolf.projectworld.net (b150f525.virtua.com.br [177.80.245.37]) by smtp.strato.de (jorabe mo35) (RZmta 31.11 DYNA|AUTH) with (AES128-SHA encrypted) ESMTPA id a06212p07CwTux for ; Mon, 7 Jan 2013 14:52:05 +0100 (CET) Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Apple Message framework v1283) Subject: Re: How to map device addresses into user space From: "Dr. Rolf Jansen" In-Reply-To: Date: Mon, 7 Jan 2013 11:52:01 -0200 Content-Transfer-Encoding: quoted-printable Message-Id: <033249A9-9C77-426F-BCA2-3A22CC4F9391@cyclaero.com> References: To: freebsd-drivers@freebsd.org X-Mailer: Apple Mail (2.1283) X-BeenThere: freebsd-drivers@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Writing device drivers for FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 Jan 2013 13:52:10 -0000 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