summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2004-06-20 20:49:58 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2004-06-20 20:49:58 +0000
commit0e10633f916b4c04f9ae43aecce849413ca76d02 (patch)
treef857166654edf34c25963856cdceadb83eb96754
parent08654bfab44aaf6923b0054d764ae829837ae77b (diff)
Port the rfx(4) frame buffer driver to sparc64.
-rw-r--r--sys/arch/sparc64/conf/GENERIC5
-rw-r--r--sys/dev/sbus/files.sbus6
-rw-r--r--sys/dev/sbus/rfx.c606
3 files changed, 615 insertions, 2 deletions
diff --git a/sys/arch/sparc64/conf/GENERIC b/sys/arch/sparc64/conf/GENERIC
index bb5d6fa7bd9..fdad36f8102 100644
--- a/sys/arch/sparc64/conf/GENERIC
+++ b/sys/arch/sparc64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.93 2004/06/20 18:15:24 miod Exp $
+# $OpenBSD: GENERIC,v 1.94 2004/06/20 20:49:56 miod Exp $
# $NetBSD: GENERIC32,v 1.18 2001/07/20 00:07:12 eeh Exp $
machine sparc64
@@ -270,6 +270,9 @@ wsdisplay* at cgthree?
zx* at sbus? # Sun Leo (ZX) framebuffer
wsdisplay* at zx?
+rfx* at sbus? # RasterFlex framebuffer series
+wsdisplay* at rfx?
+
audiocs* at sbus?
asio* at sbus? # Aurora 210SJ
diff --git a/sys/dev/sbus/files.sbus b/sys/dev/sbus/files.sbus
index 81bdae22617..47df6b833be 100644
--- a/sys/dev/sbus/files.sbus
+++ b/sys/dev/sbus/files.sbus
@@ -1,4 +1,4 @@
-# $OpenBSD: files.sbus,v 1.24 2004/06/20 18:15:25 miod Exp $
+# $OpenBSD: files.sbus,v 1.25 2004/06/20 20:49:57 miod Exp $
# $NetBSD: files.sbus,v 1.16 2000/12/08 17:29:12 martin Exp $
#
# Config file and device description for machine-independent SBUS code.
@@ -68,6 +68,10 @@ device zx: wsemuldisplaydev, rasops32, wsemul_sun
attach zx at sbus
file dev/sbus/zx.c zx
+device rfx: wsemuldisplaydev, rasops8, wsemul_sun
+attach rfx at sbus
+file dev/sbus/rfx.c rfx
+
device magma {}
attach magma at sbus
device mtty
diff --git a/sys/dev/sbus/rfx.c b/sys/dev/sbus/rfx.c
new file mode 100644
index 00000000000..db5cbc5de64
--- /dev/null
+++ b/sys/dev/sbus/rfx.c
@@ -0,0 +1,606 @@
+/* $OpenBSD: rfx.c,v 1.1 2004/06/20 20:49:57 miod Exp $ */
+
+/*
+ * Copyright (c) 2004, Miodrag Vallat.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Driver for the Vitec RasterFlex family of frame buffers.
+ * It should support RasterFlex-24, RasterFlex-32 and RasterFlex-HR.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/tty.h>
+#include <sys/conf.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/autoconf.h>
+#include <machine/pmap.h>
+#include <machine/cpu.h>
+#include <machine/conf.h>
+#include <machine/openfirm.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsdisplayvar.h>
+#include <dev/wscons/wscons_raster.h>
+#include <dev/rasops/rasops.h>
+#include <machine/fbvar.h>
+
+#include <dev/sbus/sbusvar.h>
+
+#include <dev/ic/bt463reg.h>
+
+/*
+ * Configuration structure
+ */
+struct rfx_config {
+ u_int16_t unknown;
+ u_int16_t version;
+ u_int32_t scanline;
+ u_int32_t maxwidth; /* unsure */
+ u_int32_t maxheight; /* unsure */
+ u_int32_t width;
+ u_int32_t height;
+};
+
+/*
+ * In-memory offsets
+ */
+
+#define RFX_RAMDAC_ADDR 0x00020000
+#define RFX_RAMDAC_SIZE 0x00000004
+
+#define RFX_CONTROL_ADDR 0x00040000
+#define RFX_CONTROL_SIZE 0x000000e0
+
+#define RFX_INIT_ADDR 0x00018000
+#define RFX_INIT_OFFSET 0x0000001c
+#define RFX_INIT_SIZE 0x00008000
+
+#define RFX_VRAM_ADDR 0x00100000
+
+/*
+ * Control registers
+ */
+
+#define RFX_VIDCTRL_REG 0x10
+#define RFX_VSYNC_ENABLE 0x00000001
+#define RFX_VIDEO_DISABLE 0x00000002
+
+/*
+ * Shadow colormap
+ */
+struct rfx_cmap {
+ u_int8_t red[256];
+ u_int8_t green[256];
+ u_int8_t blue[256];
+};
+
+struct rfx_softc {
+ struct sunfb sc_sunfb;
+ struct sbusdev sc_sd;
+
+ bus_space_tag_t sc_bustag;
+ bus_addr_t sc_paddr;
+
+ struct intrhand sc_ih;
+
+ struct rfx_cmap sc_cmap;
+ volatile u_int8_t *sc_ramdac;
+ volatile u_int32_t *sc_ctrl;
+
+ int sc_nscreens;
+};
+
+struct wsscreen_descr rfx_stdscreen = {
+ "std",
+};
+
+const struct wsscreen_descr *rfx_scrlist[] = {
+ &rfx_stdscreen,
+};
+
+struct wsscreen_list rfx_screenlist = {
+ sizeof(rfx_scrlist) / sizeof(struct wsscreen_descr *),
+ rfx_scrlist
+};
+
+int rfx_alloc_screen(void *, const struct wsscreen_descr *, void **,
+ int *, int *, long *);
+void rfx_burner(void *, u_int, u_int);
+void rfx_free_screen(void *, void *);
+int rfx_ioctl(void *, u_long, caddr_t, int, struct proc *);
+int rfx_show_screen(void *, void *, int, void (*cb)(void *, int, int),
+ void *);
+paddr_t rfx_mmap(void *, off_t, int);
+
+int rfx_getcmap(struct rfx_cmap *, struct wsdisplay_cmap *);
+int rfx_initialize(struct rfx_softc *, struct sbus_attach_args *,
+ struct rfx_config *);
+int rfx_intr(void *);
+void rfx_loadcmap(struct rfx_softc *, int, int);
+int rfx_putcmap(struct rfx_cmap *, struct wsdisplay_cmap *);
+void rfx_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
+
+struct wsdisplay_accessops rfx_accessops = {
+ rfx_ioctl,
+ rfx_mmap,
+ rfx_alloc_screen,
+ rfx_free_screen,
+ rfx_show_screen,
+ NULL, /* load_font */
+ NULL, /* scrollback */
+ NULL, /* getchar */
+ rfx_burner,
+};
+
+int rfxmatch(struct device *, void *, void *);
+void rfxattach(struct device *, struct device *, void *);
+
+#if defined(OpenBSD)
+struct cfattach rfx_ca = {
+ sizeof (struct rfx_softc), rfxmatch, rfxattach
+};
+
+struct cfdriver rfx_cd = {
+ NULL, "rfx", DV_DULL
+};
+#else
+CFATTACH_DECL(rfx, sizeof (struct rfx_softc), rfxmatch, rfxattach, NULL, NULL);
+#endif
+
+/*
+ * Match a supported RasterFlex card.
+ */
+int
+rfxmatch(struct device *parent, void *vcf, void *aux)
+{
+ struct sbus_attach_args *sa = aux;
+ const char *device = sa->sa_name;
+
+ /* skip vendor name (could be CWARE, VITec, ...) */
+ while (*device != ',' && *device != '\0')
+ device++;
+ if (*device == '\0')
+ device = sa->sa_name;
+ else
+ device++;
+
+ if (strncmp(device, "RasterFLEX", strlen("RasterFLEX")) != 0)
+ return (0);
+
+ /* RasterVideo and RasterFlex-TV are frame grabbers */
+ if (strcmp(device, "RasterFLEX-TV") == 0)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Attach and initialize a rfx display, as well as a child wsdisplay.
+ */
+void
+rfxattach(struct device *parent, struct device *self, void *args)
+{
+ struct rfx_softc *sc = (struct rfx_softc *)self;
+ struct sbus_attach_args *sa = args;
+ const char *device = sa->sa_name;
+ struct rfx_config cf;
+ struct wsemuldisplaydev_attach_args waa;
+ bus_space_tag_t bt;
+ bus_space_handle_t bh;
+ int node, cflen, isconsole = 0;
+
+ /* skip vendor name (could be CWARE, VITec, ...) */
+ while (*device != ',' && *device != '\0')
+ device++;
+ if (*device == '\0')
+ device = sa->sa_name;
+ else
+ device++;
+
+ printf(": %s", device);
+
+ if (sa->sa_nreg == 0) {
+ printf("\n%s: no SBus registers!\n", self->dv_xname);
+ return;
+ }
+
+ bt = sa->sa_bustag;
+ node = sa->sa_node;
+ isconsole = node == fbnode;
+
+ /*
+ * Parse configuration structure
+ */
+ cflen = getproplen(node, "configuration");
+ if (cflen != sizeof cf) {
+ printf(", unknown %d bytes conf. structure", cflen);
+ /* fill in default values */
+ cf.version = 0;
+ cf.scanline = 2048;
+ cf.width = 1152;
+ cf.height = 900;
+ } else {
+ OF_getprop(node, "configuration", &cf, cflen);
+ printf(", revision %d", cf.version);
+ }
+
+ /*
+ * Map registers
+ */
+
+ sc->sc_bustag = bt;
+ sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset);
+
+ if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_RAMDAC_ADDR,
+ RFX_RAMDAC_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) {
+ printf("\n%s: couldn't map ramdac registers\n", self->dv_xname);
+ return;
+ }
+ sc->sc_ramdac = (u_int8_t *)bus_space_vaddr(bt, bh);
+
+ if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_CONTROL_ADDR,
+ RFX_CONTROL_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) {
+ printf("\n%s: couldn't map control registers\n", self->dv_xname);
+ return;
+ }
+ sc->sc_ctrl = (u_int32_t *)bus_space_vaddr(bt, bh);
+
+#if 0 /* not yet */
+ sc->sc_ih.ih_fun = rfx_intr;
+ sc->sc_ih.ih_arg = sc;
+ intr_establish(ca->ca_ra.ra_intr[0].int_pri, &sc->sc_ih, IPL_FB);
+#endif
+
+ /*
+ * The following is an equivalent for
+ * fb_setsize(&sc->sc_sunfb, 8, cf.width, cf.height,
+ * node, ca->ca_bustype);
+ * forcing the correct scan line value. Since the usual frame buffer
+ * properties are missing on this card, no need to go through
+ * fb_setsize()...
+ */
+ sc->sc_sunfb.sf_depth = 8;
+ sc->sc_sunfb.sf_width = cf.width;
+ sc->sc_sunfb.sf_height = cf.height;
+ sc->sc_sunfb.sf_linebytes = cf.scanline;
+ sc->sc_sunfb.sf_fbsize = cf.height * cf.scanline;
+
+ printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);
+
+ if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_VRAM_ADDR,
+ round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR,
+ 0, &bh) != 0) {
+ printf("\n%s: couldn't map video memory\n", self->dv_xname);
+ return;
+ }
+ sc->sc_sunfb.sf_ro.ri_bits = bus_space_vaddr(bt, bh);
+ sc->sc_sunfb.sf_ro.ri_hw = sc;
+
+ /*
+ * If we are not the console, the frame buffer has not been
+ * initalized by the PROM - do this ourselves.
+ */
+ if (!isconsole) {
+ if (rfx_initialize(sc, sa, &cf) != 0)
+ return;
+ }
+
+ fbwscons_init(&sc->sc_sunfb, isconsole ? 0 : RI_CLEAR);
+
+ bzero(&sc->sc_cmap, sizeof(sc->sc_cmap));
+ fbwscons_setcolormap(&sc->sc_sunfb, rfx_setcolor);
+
+ rfx_stdscreen.capabilities = sc->sc_sunfb.sf_ro.ri_caps;
+ rfx_stdscreen.nrows = sc->sc_sunfb.sf_ro.ri_rows;
+ rfx_stdscreen.ncols = sc->sc_sunfb.sf_ro.ri_cols;
+ rfx_stdscreen.textops = &sc->sc_sunfb.sf_ro.ri_ops;
+
+ if (isconsole) {
+ fbwscons_console_init(&sc->sc_sunfb, &rfx_stdscreen, -1,
+ rfx_burner);
+ }
+
+ /* enable video */
+ rfx_burner(sc, 1, 0);
+
+ sbus_establish(&sc->sc_sd, &sc->sc_sunfb.sf_dev);
+
+ waa.console = isconsole;
+ waa.scrdata = &rfx_screenlist;
+ waa.accessops = &rfx_accessops;
+ waa.accesscookie = sc;
+ config_found(self, &waa, wsemuldisplaydevprint);
+}
+
+/*
+ * Common wsdisplay operations
+ */
+
+int
+rfx_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
+{
+ struct rfx_softc *sc = v;
+ struct wsdisplay_cmap *cm;
+ struct wsdisplay_fbinfo *wdf;
+ int error;
+
+ switch (cmd) {
+ case WSDISPLAYIO_GTYPE:
+ *(u_int *)data = WSDISPLAY_TYPE_RFLEX;
+ break;
+ case WSDISPLAYIO_GINFO:
+ wdf = (struct wsdisplay_fbinfo *)data;
+ wdf->height = sc->sc_sunfb.sf_height;
+ wdf->width = sc->sc_sunfb.sf_width;
+ wdf->depth = sc->sc_sunfb.sf_depth;
+ wdf->cmsize = 256;
+ break;
+ case WSDISPLAYIO_LINEBYTES:
+ *(u_int *)data = sc->sc_sunfb.sf_linebytes;
+ break;
+
+ case WSDISPLAYIO_GETCMAP:
+ cm = (struct wsdisplay_cmap *)data;
+ error = rfx_getcmap(&sc->sc_cmap, cm);
+ if (error != 0)
+ return (error);
+ break;
+ case WSDISPLAYIO_PUTCMAP:
+ cm = (struct wsdisplay_cmap *)data;
+ error = rfx_putcmap(&sc->sc_cmap, cm);
+ if (error != 0)
+ return (error);
+ rfx_loadcmap(sc, cm->index, cm->count);
+ break;
+
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+paddr_t
+rfx_mmap(void *v, off_t offset, int prot)
+{
+ struct rfx_softc *sc = v;
+
+ if (offset & PGOFSET)
+ return (-1);
+
+ if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) {
+ return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr,
+ RFX_VRAM_ADDR + offset, prot, BUS_SPACE_MAP_LINEAR));
+ }
+
+ return (-1);
+}
+
+int
+rfx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
+ int *curxp, int *curyp, long *attrp)
+{
+ struct rfx_softc *sc = v;
+
+ if (sc->sc_nscreens > 0)
+ return (ENOMEM);
+
+ *cookiep = &sc->sc_sunfb.sf_ro;
+ *curyp = 0;
+ *curxp = 0;
+ sc->sc_sunfb.sf_ro.ri_ops.alloc_attr(&sc->sc_sunfb.sf_ro,
+ WSCOL_BLACK, WSCOL_WHITE, WSATTR_WSCOLORS, attrp);
+ sc->sc_nscreens++;
+ return (0);
+}
+
+void
+rfx_free_screen(void *v, void *cookie)
+{
+ struct rfx_softc *sc = v;
+
+ sc->sc_nscreens--;
+}
+
+int
+rfx_show_screen(void *v, void *cookie, int waitok,
+ void (*cb)(void *, int, int), void *cbarg)
+{
+ return (0);
+}
+
+void
+rfx_burner(void *v, u_int on, u_int flags)
+{
+ struct rfx_softc *sc = v;
+
+ if (on) {
+ sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VIDEO_DISABLE;
+ sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VSYNC_ENABLE;
+ } else {
+ sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VIDEO_DISABLE;
+ if (flags & WSDISPLAY_BURN_VBLANK)
+ sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VSYNC_ENABLE;
+ }
+}
+
+/*
+ * Colormap helper functions
+ */
+
+void
+rfx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
+{
+ struct rfx_softc *sc = v;
+
+ sc->sc_cmap.red[index] = r;
+ sc->sc_cmap.green[index] = g;
+ sc->sc_cmap.blue[index] = b;
+
+ rfx_loadcmap(sc, index, 1);
+}
+
+int
+rfx_getcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm)
+{
+ u_int index = rcm->index, count = rcm->count;
+ int error;
+
+ if (index >= 256 || count > 256 - index)
+ return (EINVAL);
+
+ if ((error = copyout(cm->red + index, rcm->red, count)) != 0)
+ return (error);
+ if ((error = copyout(cm->green + index, rcm->green, count)) != 0)
+ return (error);
+ if ((error = copyout(cm->blue + index, rcm->blue, count)) != 0)
+ return (error);
+
+ return (0);
+}
+
+int
+rfx_putcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm)
+{
+ u_int index = rcm->index, count = rcm->count;
+ u_int8_t red[256], green[256], blue[256];
+ int error;
+
+ if (index >= 256 || count > 256 - index)
+ return (EINVAL);
+
+ if ((error = copyin(rcm->red, red, count)) != 0)
+ return (error);
+ if ((error = copyin(rcm->green, green, count)) != 0)
+ return (error);
+ if ((error = copyin(rcm->blue, blue, count)) != 0)
+ return (error);
+
+ bcopy(red, cm->red + index, count);
+ bcopy(green, cm->green + index, count);
+ bcopy(blue, cm->blue + index, count);
+
+ return (0);
+}
+
+void
+rfx_loadcmap(struct rfx_softc *sc, int start, int ncolors)
+{
+ u_int8_t *r, *g, *b;
+
+ r = sc->sc_cmap.red + start;
+ g = sc->sc_cmap.green + start;
+ b = sc->sc_cmap.blue + start;
+
+ start += BT463_IREG_CPALETTE_RAM;
+ sc->sc_ramdac[BT463_REG_ADDR_LOW] = start & 0xff;
+ sc->sc_ramdac[BT463_REG_ADDR_HIGH] = (start >> 8) & 0xff;
+
+ while (ncolors-- != 0) {
+ sc->sc_ramdac[BT463_REG_CMAP_DATA] = *r++;
+ sc->sc_ramdac[BT463_REG_CMAP_DATA] = *g++;
+ sc->sc_ramdac[BT463_REG_CMAP_DATA] = *b++;
+ }
+}
+
+/*
+ * Initialization code parser
+ */
+
+int
+rfx_initialize(struct rfx_softc *sc, struct sbus_attach_args *sa,
+ struct rfx_config *cf)
+{
+ u_int32_t *data, offset, value;
+ size_t cnt;
+ bus_space_handle_t bh;
+ int error;
+
+ /*
+ * Map the initialization data
+ */
+ if ((error = sbus_bus_map(sa->sa_bustag, sa->sa_slot, sa->sa_offset +
+ RFX_INIT_ADDR, RFX_INIT_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh)) != 0) {
+ printf("\n%s: couldn't map initialization data\n",
+ sc->sc_sunfb.sf_dev.dv_xname);
+ return error;
+ }
+ data = (u_int32_t *)bus_space_vaddr(sa->sa_bustag, bh);
+
+ /*
+ * Skip copyright notice
+ */
+ data += RFX_INIT_OFFSET / sizeof(u_int32_t);
+ cnt = RFX_INIT_SIZE - RFX_INIT_OFFSET / sizeof(u_int32_t);
+ cnt >>= 1;
+
+ /*
+ * Parse and apply settings
+ */
+ while (cnt != 0) {
+ offset = *data++;
+ value = *data++;
+
+ if (offset == (u_int32_t)-1 && value == (u_int32_t)-1)
+ break;
+
+ /* Old PROM are little-endian */
+ if (cf->version <= 1) {
+ offset = letoh32(offset);
+ value = letoh32(offset);
+ }
+
+ if (offset & (1 << 31)) {
+ offset = (offset & ~(1 << 31)) - RFX_RAMDAC_ADDR;
+ if (offset < RFX_RAMDAC_SIZE)
+ sc->sc_ramdac[offset] = value >> 24;
+ } else {
+ offset -= RFX_CONTROL_ADDR;
+ if (offset < RFX_CONTROL_SIZE)
+ sc->sc_ctrl[offset >> 2] = value;
+ }
+
+ cnt--;
+ }
+
+#ifdef DEBUG
+ if (cnt == 0)
+ printf("%s: incoherent initialization data!\n");
+#endif
+
+ bus_space_unmap(sa->sa_bustag, bh, RFX_INIT_SIZE);
+
+ return 0;
+}