Subject: IBM ServeRAID driver, the first attempt
To: None <tech-kern@netbsd.org>
From: Tonnerre LOMBARD <tonnerre@bsdprojects.net>
List: tech-kern
Date: 05/15/2007 21:21:38
--rJwd6BRFiFCcLxzm
Content-Type: multipart/mixed; boundary="WIyZ46R2i8wDzkSu"
Content-Disposition: inline


--WIyZ46R2i8wDzkSu
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline

Salut,

This patch adds support for the IBM ServeRAID. It compiles, but I don't
know for sure yet whether it boots. I only wanted to share the first
experience.

(The reason why I don't know yet whether it boots is that my Netfinity
needs some special config flags in order to boot at all, which I forgot
to set)

Still to do:
 * Test whether it actually works ;-)
 * Add it to the config files of other arches
 * Make it MP safe once the SMP patches are that far

				Tonnerre

--WIyZ46R2i8wDzkSu
Content-Type: text/plain; charset=utf-8
Content-Description: The patch adding IBM ServeRAID support
Content-Disposition: attachment; filename="ips.diff"
Content-Transfer-Encoding: quoted-printable

Index: arch/i386/conf/ALL
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/arch/i386/conf/ALL,v
retrieving revision 1.73.2.4
diff -u -r1.73.2.4 arch/i386/conf/ALL
--- arch/i386/conf/ALL	20 Feb 2007 14:12:08 -0000	1.73.2.4
+++ arch/i386/conf/ALL	15 May 2007 02:06:54 -0000
@@ -632,6 +632,7 @@
 bha*	at pci? dev ? function ?	# BusLogic 9xx SCSI
 dpt*	at pci? dev ? function ?	# DPT SmartCache/SmartRAID
 iha*	at pci? dev ? function ?	# Initio INIC-940/950 SCSI
+ips*	at pci? dev ? function ?	# IBM ServeRAID and compatible
 isp*	at pci? dev ? function ?	# Qlogic ISP [12]0x0 SCSI/FibreChannel
 mfi*	at pci? dev ? function ?	# LSI MegaRAID SAS
 mly*	at pci? dev ? function ?	# Mylex AcceleRAID and eXtremeRAID
Index: arch/i386/conf/GENERIC
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/arch/i386/conf/GENERIC,v
retrieving revision 1.799.2.5
diff -u -r1.799.2.5 arch/i386/conf/GENERIC
--- arch/i386/conf/GENERIC	22 Mar 2007 20:00:09 -0000	1.799.2.5
+++ arch/i386/conf/GENERIC	15 May 2007 02:06:54 -0000
@@ -640,6 +640,7 @@
 bha*	at pci? dev ? function ?	# BusLogic 9xx SCSI
 dpt*	at pci? dev ? function ?	# DPT SmartCache/SmartRAID
 iha*	at pci? dev ? function ?	# Initio INIC-940/950 SCSI
+ips*	at pci? dev ? function ?	# IBM ServeRAID and compatible
 isp*	at pci? dev ? function ?	# Qlogic ISP [12]0x0 SCSI/FibreChannel
 mfi*	at pci? dev ? function ?	# LSI MegaRAID SAS
 mly*	at pci? dev ? function ?	# Mylex AcceleRAID and eXtremeRAID
Index: arch/i386/conf/INSTALL
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/arch/i386/conf/INSTALL,v
retrieving revision 1.297.2.4
diff -u -r1.297.2.4 arch/i386/conf/INSTALL
--- arch/i386/conf/INSTALL	28 Jan 2007 20:05:41 -0000	1.297.2.4
+++ arch/i386/conf/INSTALL	15 May 2007 02:06:54 -0000
@@ -339,6 +339,7 @@
 bha*	at pci? dev ? function ?	# BusLogic 9xx SCSI
 dpt*	at pci? dev ? function ?	# DPT SmartCache/SmartRAID
 iha*	at pci? dev ? function ?	# Initio INIC-940/950 SCSI
+ips*	at pci? dev ? function ?	# IBM ServeRAID and compatible
 isp*	at pci? dev ? function ?	# Qlogic ISP [12]0x0 SCSI/FibreChannel
 mfi*	at pci? dev ? function ?	# LSI MegaRAID SAS
 mly*	at pci? dev ? function ?	# Mylex AcceleRAID and eXtremeRAID
Index: arch/i386/conf/XEN2_DOM0
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/arch/i386/conf/XEN2_DOM0,v
retrieving revision 1.18.2.3
diff -u -r1.18.2.3 arch/i386/conf/XEN2_DOM0
--- arch/i386/conf/XEN2_DOM0	19 Jan 2007 22:28:04 -0000	1.18.2.3
+++ arch/i386/conf/XEN2_DOM0	15 May 2007 02:06:54 -0000
@@ -409,6 +409,7 @@
 bha*	at pci? dev ? function ?	# BusLogic 9xx SCSI
 dpt*	at pci? dev ? function ?	# DPT SmartCache/SmartRAID
 iha*	at pci? dev ? function ?	# Initio INIC-940/950 SCSI
+ips*	at pci? dev ? function ?	# IBM ServeRAID and compatible
 isp*	at pci? dev ? function ?	# Qlogic ISP [12]0x0 SCSI/FibreChannel
 mfi*	at pci? dev ? function ?	# LSI MegaRAID SAS
 mly*	at pci? dev ? function ?	# Mylex AcceleRAID and eXtremeRAID
Index: dev/pci/files.pci
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/dev/pci/files.pci,v
retrieving revision 1.273.2.2
diff -u -r1.273.2.2 files.pci
--- dev/pci/files.pci	21 Dec 2006 13:44:01 -0000	1.273.2.2
+++ dev/pci/files.pci	15 May 2007 02:00:26 -0000
@@ -142,6 +142,11 @@
 attach	mpt at pci with mpt_pci
 file	dev/pci/mpt_pci.c		mpt_pci
=20
+# IBM ServeRAID controllers
+device	ips: scsi
+attach	ips at pci
+file	dev/pci/ips.c			ips
+
 # Ethernet driver for DC21040-based boards
 device	de: ether, ifnet, arp
 attach	de at pci
Index: dev/pci/ips.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/dev/pci/ips.c,v
--- /dev/null	2007-05-15 05:51:52.000000000 +0200
+++ dev/pci/ips.c	2007-05-15 05:34:10.000000000 +0200
@@ -0,0 +1,830 @@
+/*	$OpenBSD: ips.c,v 1.16 2006/11/29 18:18:39 grange Exp $	*/
+
+/*
+ * Copyright (c) 2006 Alexander Yurchenko <grange@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * IBM ServeRAID controller driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+
+#include <machine/bus.h>
+
+/* SCSI includes go here */
+#include <dev/scsipi/scsi_all.h>
+#include <dev/scsipi/scsi_disk.h>
+#include <dev/scsipi/scsipi_all.h>
+#include <dev/scsipi/scsipi_disk.h>
+#include <dev/scsipi/scsiconf.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#define IPS_DEBUG	/* XXX: remove when the driver becomes stable */
+
+/* Debug levels */
+#define IPS_D_ERR	0x0001
+#define IPS_D_INFO	0x0002
+#define IPS_D_XFER	0x0004
+#define IPS_D_INTR	0x0008
+
+#ifdef IPS_DEBUG
+#define DPRINTF(a, b)	if (ips_debug & (a)) printf b
+int ips_debug =3D IPS_D_ERR;
+#else
+#define DPRINTF(a, b)
+#endif
+
+/*
+ * Register definitions.
+ */
+#define IPS_BAR0		0x10	/* I/O space base address */
+#define IPS_BAR1		0x14	/* I/O space base address */
+
+#define IPS_MORPHEUS_OISR	0x0030	/* outbound IRQ status */
+#define	IPS_MORPHEUS_OISR_CMD		(1 << 3)
+#define IPS_MORPHEUS_OIMR	0x0034	/* outbound IRQ mask */
+#define IPS_MORPHEUS_IQPR	0x0040	/* inbound queue port */
+#define IPS_MORPHEUS_OQPR	0x0044	/* outbound queue port */
+#define IPS_MORPHEUS_OQPR_ID(x)		(((x) >> 8) & 0xff)
+#define IPS_MORPHEUS_OQPR_ST(x)		(((x) >> 16) & 0xff)
+#define IPS_MORPHEUS_OQPR_GSC(x)	(((x) >> 16) & 0x0f)
+#define IPS_MORPHEUS_OQPR_EST(x)	(((x) >> 24) & 0xff)
+#define IPS_MORPHEUS_GSC_NOERR		0x0
+#define IPS_MORPHEUS_GSC_RECOV		0x1
+
+/* Commands */
+#define IPS_CMD_READ		0x02
+#define IPS_CMD_WRITE		0x03
+#define IPS_CMD_ADAPTERINFO	0x05
+#define IPS_CMD_FLUSHCACHE	0x0a
+#define IPS_CMD_READ_SG		0x82
+#define IPS_CMD_WRITE_SG	0x83
+#define IPS_CMD_DRIVEINFO	0x19
+
+#define IPS_MAXCMDSZ		256	/* XXX: for now */
+#define IPS_MAXDATASZ		64 * 1024
+#define IPS_MAXSEGS		32
+
+#define IPS_MAXDRIVES		8
+#define IPS_MAXCHANS		4
+#define IPS_MAXTARGETS		15
+#define IPS_MAXCMDS		32
+
+#define IPS_MAXFER		(64 * 1024)
+#define IPS_MAXSGS		32
+
+/* Command frames */
+struct ips_cmd_adapterinfo {
+	u_int8_t	command;
+	u_int8_t	id;
+	u_int8_t	reserve1;
+	u_int8_t	commandtype;
+	u_int32_t	reserve2;
+	u_int32_t	buffaddr;
+	u_int32_t	reserve3;
+} __packed;
+
+struct ips_cmd_driveinfo {
+	u_int8_t	command;
+	u_int8_t	id;
+	u_int8_t	drivenum;
+	u_int8_t	reserve1;
+	u_int32_t	reserve2;
+	u_int32_t	buffaddr;
+	u_int32_t	reserve3;
+} __packed;
+
+struct ips_cmd_generic {
+	u_int8_t	command;
+	u_int8_t	id;
+	u_int8_t	drivenum;
+	u_int8_t	reserve2;
+	u_int32_t	lba;
+	u_int32_t	buffaddr;
+	u_int32_t	reserve3;
+} __packed;
+
+struct ips_cmd_io {
+	u_int8_t	command;
+	u_int8_t	id;
+	u_int8_t	drivenum;
+	u_int8_t	segnum;
+	u_int32_t	lba;
+	u_int32_t	buffaddr;
+	u_int16_t	length;
+	u_int16_t	reserve1;
+} __packed;
+
+/* Data frames */
+struct ips_adapterinfo {
+	u_int8_t	drivecount;
+	u_int8_t	miscflags;
+	u_int8_t	SLTflags;
+	u_int8_t	BSTflags;
+	u_int8_t	pwr_chg_count;
+	u_int8_t	wrong_addr_count;
+	u_int8_t	unident_count;
+	u_int8_t	nvram_dev_chg_count;
+	u_int8_t	codeblock_version[8];
+	u_int8_t	bootblock_version[8];
+	u_int32_t	drive_sector_count[IPS_MAXDRIVES];
+	u_int8_t	max_concurrent_cmds;
+	u_int8_t	max_phys_devices;
+	u_int16_t	flash_prog_count;
+	u_int8_t	defunct_disks;
+	u_int8_t	rebuildflags;
+	u_int8_t	offline_drivecount;
+	u_int8_t	critical_drivecount;
+	u_int16_t	config_update_count;
+	u_int8_t	blockedflags;
+	u_int8_t	psdn_error;
+	u_int16_t	addr_dead_disk[IPS_MAXCHANS][IPS_MAXTARGETS];
+} __packed;
+
+struct ips_drive {
+	u_int8_t	drivenum;
+	u_int8_t	merge_id;
+	u_int8_t	raid_lvl;
+	u_int8_t	state;
+	u_int32_t	sector_count;
+} __packed;
+
+struct ips_driveinfo {
+	u_int8_t	drivecount;
+	u_int8_t	reserve1;
+	u_int16_t	reserve2;
+	struct ips_drive drives[IPS_MAXDRIVES];
+} __packed;
+
+/* I/O access helper macros */
+#define IPS_READ_4(s, r) \
+	le32toh(bus_space_read_4((s)->sc_iot, (s)->sc_ioh, (r)))
+#define IPS_WRITE_4(s, r, v) \
+	bus_space_write_4((s)->sc_iot, (s)->sc_ioh, (r), htole32((v)))
+
+struct ccb {
+	int			c_id;
+	int			c_flags;
+#define CCB_F_RUN	0x0001
+
+	bus_dmamap_t		c_dmam;
+	struct scsipi_xfer *	c_xfer;
+
+	TAILQ_ENTRY(ccb)	c_link;
+};
+
+TAILQ_HEAD(ccbq, ccb);
+
+struct dmamem {
+	bus_dma_tag_t		dm_tag;
+	bus_dmamap_t		dm_map;
+	bus_dma_segment_t	dm_seg;
+	bus_size_t		dm_size;
+	void *			dm_kva;
+};
+
+struct ips_softc {
+	struct device		sc_dev;
+
+	struct scsibus_softc *	sc_scsi_bus;
+	struct scsipi_adapter	sc_adapter;
+	struct scsipi_channel	sc_channel;
+
+	pci_chipset_tag_t	sc_pc;
+	pcitag_t		sc_tag;
+
+	bus_space_tag_t		sc_iot;
+	bus_space_handle_t	sc_ioh;
+	bus_dma_tag_t		sc_dmat;
+
+	struct dmamem *		sc_cmdm;
+
+	struct ccb *		sc_ccb;
+	struct ccbq		sc_ccbq;
+
+	void *			sc_ih;
+
+	void			(*sc_exec)(struct ips_softc *);
+	void			(*sc_inten)(struct ips_softc *);
+	int			(*sc_intr)(void *);
+
+	struct ips_adapterinfo	sc_ai;
+	struct ips_driveinfo	sc_di;
+};
+
+int	ips_match(struct device *, struct cfdata *, void *);
+void	ips_attach(struct device *, struct device *, void *);
+
+void	ips_scsi_cmd(struct scsipi_channel *, scsipi_adapter_req_t, void *);
+void	ips_scsi_io(struct scsipi_xfer *, struct ips_softc *);
+static int	ips_scsi_ioctl(struct scsipi_channel *, u_long, caddr_t, int,
+	    	struct proc *);
+void	ips_scsi_minphys(struct buf *);
+
+void	ips_xfer_timeout(void *);
+
+void	ips_flushcache(struct ips_softc *);
+int	ips_getadapterinfo(struct ips_softc *, struct ips_adapterinfo *);
+int	ips_getdriveinfo(struct ips_softc *, struct ips_driveinfo *);
+
+void	ips_copperhead_exec(struct ips_softc *);
+void	ips_copperhead_inten(struct ips_softc *);
+int	ips_copperhead_intr(void *);
+
+void	ips_morpheus_exec(struct ips_softc *);
+void	ips_morpheus_inten(struct ips_softc *);
+int	ips_morpheus_intr(void *);
+
+struct ccb *	ips_ccb_alloc(bus_dma_tag_t, int);
+void		ips_ccb_free(struct ccb *, bus_dma_tag_t, int);
+
+struct dmamem *	ips_dmamem_alloc(bus_dma_tag_t, bus_size_t);
+void		ips_dmamem_free(struct dmamem *);
+
+CFATTACH_DECL(ips, sizeof(struct ips_softc),
+	ips_match, ips_attach, NULL, NULL);
+
+static const struct {
+	int vendor;
+	int product;
+} ips_ids[] =3D {
+	{ PCI_VENDOR_IBM,	PCI_PRODUCT_IBM_SERVERAID },
+	{ PCI_VENDOR_IBM,	PCI_PRODUCT_IBM_SERVERAID4 },
+	{ PCI_VENDOR_ADP2,	PCI_PRODUCT_ADP2_SERVERAID },
+	{ 0,			0 }
+};
+
+int
+ips_match(struct device *parent, struct cfdata *match, void *aux)
+{
+	struct pci_attach_args *pa =3D aux;
+	pcireg_t reg =3D pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
+	int i;
+
+	for (i =3D 0; ips_ids[i].vendor; i++)
+	{
+		if ((PCI_VENDOR(pa->pa_id) =3D=3D ips_ids[i].vendor &&
+		     PCI_PRODUCT(pa->pa_id) =3D=3D ips_ids[i].product) ||
+		    (PCI_VENDOR(reg) =3D=3D ips_ids[i].vendor &&
+		     PCI_PRODUCT(reg) =3D=3D ips_ids[i].product))
+			return 1;
+	}
+
+	return 0;
+}
+
+void
+ips_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct ips_softc *sc =3D (struct ips_softc *)self;
+	struct pci_attach_args *pa =3D aux;
+	int bar;
+	pcireg_t maptype;
+	bus_size_t iosize;
+	pci_intr_handle_t ih;
+	const char *intrstr;
+	int i, maxcmds;
+
+	sc->sc_pc =3D pa->pa_pc;
+	sc->sc_tag =3D pa->pa_tag;
+	sc->sc_dmat =3D pa->pa_dmat;
+
+	/* Identify the chipset */
+	switch (PCI_PRODUCT(pa->pa_id)) {
+	case PCI_PRODUCT_IBM_SERVERAID:
+		printf(": Copperhead");
+		sc->sc_exec =3D ips_copperhead_exec;
+		sc->sc_inten =3D ips_copperhead_inten;
+		sc->sc_intr =3D ips_copperhead_intr;
+		break;
+	case PCI_PRODUCT_IBM_SERVERAID4:
+	case PCI_PRODUCT_ADP2_SERVERAID:
+		printf(": Morpheus");
+		sc->sc_exec =3D ips_morpheus_exec;
+		sc->sc_inten =3D ips_morpheus_inten;
+		sc->sc_intr =3D ips_morpheus_intr;
+		break;
+	}
+
+	/* Map I/O space */
+	if (PCI_PRODUCT(pa->pa_id) =3D=3D PCI_PRODUCT_IBM_SERVERAID)
+		bar =3D IPS_BAR1;
+	else
+		bar =3D IPS_BAR0;
+	maptype =3D pci_mapreg_type(sc->sc_pc, sc->sc_tag, bar);
+	if (pci_mapreg_map(pa, bar, maptype, 0, &sc->sc_iot, &sc->sc_ioh,
+	    NULL, &iosize)) {
+		printf(": can't map I/O space\n");
+		return;
+	}
+
+	/* Allocate command DMA buffer */
+	if ((sc->sc_cmdm =3D ips_dmamem_alloc(sc->sc_dmat,
+	    IPS_MAXCMDSZ)) =3D=3D NULL) {
+		printf(": can't alloc command DMA buffer\n");
+		goto fail1;
+	}
+
+	/* Get adapter info */
+	if (ips_getadapterinfo(sc, &sc->sc_ai)) {
+		printf(": can't get adapter info\n");
+		goto fail2;
+	}
+
+	/* Get logical drives info */
+	if (ips_getdriveinfo(sc, &sc->sc_di)) {
+		printf(": can't get drives info\n");
+		goto fail2;
+	}
+
+	/* Allocate command queue */
+	maxcmds =3D sc->sc_ai.max_concurrent_cmds;
+	if ((sc->sc_ccb =3D ips_ccb_alloc(sc->sc_dmat, maxcmds)) =3D=3D NULL) {
+		printf(": can't alloc command queue\n");
+		goto fail2;
+	}
+
+	TAILQ_INIT(&sc->sc_ccbq);
+	for (i =3D 0; i < maxcmds; i++)
+		TAILQ_INSERT_TAIL(&sc->sc_ccbq, &sc->sc_ccb[i], c_link);
+
+	/* Install interrupt handler */
+	if (pci_intr_map(pa, &ih)) {
+		printf(": can't map interrupt\n");
+		goto fail3;
+	}
+	intrstr =3D pci_intr_string(sc->sc_pc, ih);
+	if ((sc->sc_ih =3D pci_intr_establish(sc->sc_pc, ih, IPL_BIO,
+	    sc->sc_intr, sc)) =3D=3D NULL) {
+		printf(": can't establish interrupt");
+		if (intrstr !=3D NULL)
+			printf(" at %s", intrstr);
+		printf("\n");
+		goto fail3;
+	}
+	printf(", %s\n", intrstr);
+
+	/* Enable interrupts */
+	(*sc->sc_inten)(sc);
+
+	/* Attach SCSI bus */
+	sc->sc_channel.chan_adapter =3D &sc->sc_adapter;
+	sc->sc_channel.chan_bustype =3D &scsi_bustype;
+	sc->sc_channel.chan_channel =3D 0;
+	sc->sc_channel.chan_ntargets =3D IPS_MAXTARGETS;
+	sc->sc_channel.chan_nluns =3D 8;
+	sc->sc_channel.chan_flags =3D 0;
+	sc->sc_channel.chan_id =3D IPS_MAXTARGETS;
+	/* XXX: What about chan_openings? */
+
+	sc->sc_adapter.adapt_dev =3D (struct device *) sc;
+	sc->sc_adapter.adapt_max_periph =3D IPS_MAXTARGETS;
+	/* XXX: What about adapt_openings? */
+	sc->sc_adapter.adapt_request =3D &ips_scsi_cmd;
+	sc->sc_adapter.adapt_minphys =3D &ips_scsi_minphys;
+	sc->sc_adapter.adapt_ioctl =3D &ips_scsi_ioctl;
+
+	/* sc->sc_scsi_bus =3D (struct scsibus_softc *)config_found(self, &saa,
+	    scsiprint); */
+
+	return;
+fail3:
+	ips_ccb_free(sc->sc_ccb, sc->sc_dmat, maxcmds);
+fail2:
+	ips_dmamem_free(sc->sc_cmdm);
+fail1:
+	bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
+}
+
+void
+ips_scsi_cmd(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *=
arg)
+{
+	struct scsipi_xfer *xs =3D (struct scsipi_xfer *) arg;
+	struct scsipi_periph *periph =3D xs->xs_periph;
+	struct ips_softc *sc =3D
+		(struct ips_softc *) chan->chan_adapter->adapt_dev;
+	struct scsipi_inquiry_data *inq;
+	struct scsipi_read_capacity_10_data *cap;
+	struct scsi_sense_data *sns;
+	int target =3D periph->periph_target;
+	int s;
+
+	if (target >=3D sc->sc_di.drivecount || periph->periph_lun !=3D 0)
+		goto error;
+
+	switch (xs->cmd->opcode) {
+	case READ_10:
+	case SCSI_READ_6_COMMAND:
+	case WRITE_10:
+	case SCSI_WRITE_6_COMMAND:
+		ips_scsi_io(xs, sc);
+		return;
+	case INQUIRY:
+		inq =3D (void *)xs->data;
+		bzero(inq, sizeof(*inq));
+		inq->device =3D T_DIRECT;
+		inq->version =3D 2;
+		inq->response_format =3D 2;
+		inq->additional_length =3D 32;
+		strlcpy(inq->vendor, "IBM", sizeof(inq->vendor));
+		snprintf(inq->product, sizeof(inq->product),
+		    "ServeRAID LD %02d", target);
+		goto done;
+	case READ_CAPACITY_10:
+		cap =3D (void *)xs->data;
+		bzero(cap, sizeof(*cap));
+		_lto4b(sc->sc_di.drives[target].sector_count - 1, cap->addr);
+		_lto4b(512, cap->length);
+		goto done;
+	case REQUEST_SENSE:
+		sns =3D (void *)xs->data;
+		bzero(sns, sizeof(*sns));
+		sns->response_code =3D 0x70;
+		sns->flags =3D SKEY_NO_SENSE;
+		goto done;
+	case SCSI_SYNCHRONIZE_CACHE_10:
+		ips_flushcache(sc);
+		goto done;
+	case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
+	case START_STOP:
+	case TEST_UNIT_READY:
+		return;
+	}
+
+error:
+	xs->error =3D XS_DRIVER_STUFFUP;
+done:
+	s =3D splbio();
+	scsipi_done(xs);
+	splx(s);
+}
+
+void
+ips_scsi_io(struct scsipi_xfer *xs, struct ips_softc *sc)
+{
+	struct scsipi_periph *periph =3D xs->xs_periph;
+	struct scsipi_rw_10 *rw;
+	struct scsipi_rw_16 *rwb;
+	struct ccb *ccb;
+	struct ips_cmd_io *cmd;
+	u_int32_t blkno, blkcnt;
+	int i, s;
+
+	/* Pick up the first free ccb */
+	s =3D splbio();
+	ccb =3D TAILQ_FIRST(&sc->sc_ccbq);
+	if (ccb !=3D NULL)
+		TAILQ_REMOVE(&sc->sc_ccbq, ccb, c_link);
+	splx(s);
+	if (ccb =3D=3D NULL)
+	{
+		DPRINTF(IPS_D_ERR, ("%s: scsi_io, no free ccb\n",
+		    sc->sc_dev.dv_xname));
+		return;
+	}
+
+	DPRINTF(IPS_D_XFER, ("%s: scsi_io, ccb id %d\n", sc->sc_dev.dv_xname,
+	    ccb->c_id));
+
+	bus_dmamap_load(sc->sc_dmat, ccb->c_dmam, xs->data, xs->datalen, NULL,
+	    BUS_DMA_NOWAIT);
+	ccb->c_xfer =3D xs;
+
+	if (xs->cmd->opcode =3D=3D SCSI_READ_6_COMMAND ||
+	    xs->cmd->opcode =3D=3D SCSI_WRITE_6_COMMAND) {
+		u_int32_t length;
+		rw =3D (void *)xs->cmd;
+		length =3D _4btol(rw->length);
+		blkno =3D _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff);
+		blkcnt =3D length > 0 ? length : 0x100;
+	} else {
+		rwb =3D (void *)xs->cmd;
+		blkno =3D _4btol(rwb->addr);
+		blkcnt =3D _2btol(rwb->length);
+	}
+
+	cmd =3D sc->sc_cmdm->dm_kva;
+	bzero(cmd, sizeof(*cmd));
+	cmd->command =3D (xs->xs_control & XS_CTL_DATA_IN) ? IPS_CMD_READ :
+	    IPS_CMD_WRITE;
+	cmd->id =3D ccb->c_id;
+	cmd->drivenum =3D periph->periph_target;
+	cmd->lba =3D blkno;
+	cmd->length =3D blkcnt;
+	if (ccb->c_dmam->dm_nsegs > 1) {
+		cmd->command =3D (xs->xs_control & XS_CTL_DATA_IN) ?
+			IPS_CMD_READ_SG : IPS_CMD_WRITE_SG;
+		cmd->segnum =3D ccb->c_dmam->dm_nsegs;
+
+		for (i =3D 0; i < ccb->c_dmam->dm_nsegs; i++) {
+			*(u_int32_t *)((u_int8_t *)sc->sc_cmdm->dm_kva + 24 +
+			    i * 8) =3D ccb->c_dmam->dm_segs[i].ds_addr;
+			*(u_int32_t *)((u_int8_t *)sc->sc_cmdm->dm_kva + 24 +
+			    i * 8 + 4) =3D ccb->c_dmam->dm_segs[i].ds_len;
+		}
+		cmd->buffaddr =3D sc->sc_cmdm->dm_seg.ds_addr + 24;
+	} else {
+		cmd->buffaddr =3D ccb->c_dmam->dm_segs[0].ds_addr;
+	}
+
+	callout_setfunc(&xs->xs_callout, ips_xfer_timeout, ccb);
+	callout_schedule(&xs->xs_callout, (xs->timeout * 1000) / hz);
+
+	s =3D splbio();
+	(*sc->sc_exec)(sc);
+	ccb->c_flags |=3D CCB_F_RUN;
+	splx(s);
+}
+
+static int
+ips_scsi_ioctl(struct scsipi_channel *chan, u_long cmd, caddr_t addr, int =
flags,
+    struct proc *p)
+{
+	return (ENOTTY);
+}
+
+void
+ips_scsi_minphys(struct buf *bp)
+{
+	minphys(bp);
+}
+
+void
+ips_xfer_timeout(void *arg)
+{
+	struct ccb *ccb =3D arg;
+	struct scsipi_xfer *xs =3D ccb->c_xfer;
+	struct ips_softc *sc =3D (struct ips_softc *) xs->xs_periph->
+		periph_channel->chan_adapter->adapt_dev;
+	int s;
+
+	DPRINTF(IPS_D_ERR, ("%s: xfer timeout, ccb id %d\n",
+	    sc->sc_dev.dv_xname, ccb->c_id));
+
+	bus_dmamap_unload(sc->sc_dmat, ccb->c_dmam);
+	xs->error =3D XS_TIMEOUT;
+	s =3D splbio();
+	scsipi_done(xs);
+	ccb->c_flags &=3D ~CCB_F_RUN;
+	TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, c_link);
+	splx(s);
+}
+
+void
+ips_flushcache(struct ips_softc *sc)
+{
+	struct ips_cmd_generic *cmd;
+
+	cmd =3D sc->sc_cmdm->dm_kva;
+	cmd->command =3D IPS_CMD_FLUSHCACHE;
+
+	(*sc->sc_exec)(sc);
+	DELAY(1000);
+}
+
+int
+ips_getadapterinfo(struct ips_softc *sc, struct ips_adapterinfo *ai)
+{
+	struct dmamem *dm;
+	struct ips_cmd_adapterinfo *cmd;
+
+	if ((dm =3D ips_dmamem_alloc(sc->sc_dmat, sizeof(*ai))) =3D=3D NULL)
+		return (1);
+
+	cmd =3D sc->sc_cmdm->dm_kva;
+	bzero(cmd, sizeof(*cmd));
+	cmd->command =3D IPS_CMD_ADAPTERINFO;
+	cmd->buffaddr =3D dm->dm_seg.ds_addr;
+
+	(*sc->sc_exec)(sc);
+	DELAY(1000);
+	bcopy(dm->dm_kva, ai, sizeof(*ai));
+	ips_dmamem_free(dm);
+
+	return (0);
+}
+
+int
+ips_getdriveinfo(struct ips_softc *sc, struct ips_driveinfo *di)
+{
+	struct dmamem *dm;
+	struct ips_cmd_driveinfo *cmd;
+
+	if ((dm =3D ips_dmamem_alloc(sc->sc_dmat, sizeof(*di))) =3D=3D NULL)
+		return (1);
+
+	cmd =3D sc->sc_cmdm->dm_kva;
+	bzero(cmd, sizeof(*cmd));
+	cmd->command =3D IPS_CMD_DRIVEINFO;
+	cmd->buffaddr =3D dm->dm_seg.ds_addr;
+
+	(*sc->sc_exec)(sc);
+	DELAY(1000);
+	bcopy(dm->dm_kva, di, sizeof(*di));
+	ips_dmamem_free(dm);
+
+	return (0);
+}
+
+void
+ips_copperhead_exec(struct ips_softc *sc)
+{
+}
+
+void
+ips_copperhead_inten(struct ips_softc *sc)
+{
+}
+
+int
+ips_copperhead_intr(void *arg)
+{
+	return (0);
+}
+
+void
+ips_morpheus_exec(struct ips_softc *sc)
+{
+	IPS_WRITE_4(sc, IPS_MORPHEUS_IQPR, sc->sc_cmdm->dm_seg.ds_addr);
+}
+
+void
+ips_morpheus_inten(struct ips_softc *sc)
+{
+	u_int32_t reg;
+
+	reg =3D IPS_READ_4(sc, IPS_MORPHEUS_OIMR);
+	reg &=3D ~0x08;
+	IPS_WRITE_4(sc, IPS_MORPHEUS_OIMR, reg);
+}
+
+int
+ips_morpheus_intr(void *arg)
+{
+	struct ips_softc *sc =3D arg;
+	struct ccb *ccb;
+	struct scsipi_xfer *xs;
+	u_int32_t oisr, oqpr;
+	int gsc, id, s, rv =3D 0;
+
+	oisr =3D IPS_READ_4(sc, IPS_MORPHEUS_OISR);
+	DPRINTF(IPS_D_INTR, ("%s: intr, OISR 0x%08x\n",
+	    sc->sc_dev.dv_xname, oisr));
+
+	if (!(oisr & IPS_MORPHEUS_OISR_CMD))
+		return (0);
+
+	while ((oqpr =3D IPS_READ_4(sc, IPS_MORPHEUS_OQPR)) !=3D 0xffffffff) {
+		DPRINTF(IPS_D_INTR, ("OQPR 0x%08x\n", oqpr));
+
+		gsc =3D IPS_MORPHEUS_OQPR_GSC(oqpr);
+		id =3D IPS_MORPHEUS_OQPR_ID(oqpr);
+
+		if (gsc !=3D IPS_MORPHEUS_GSC_NOERR &&
+		    gsc !=3D IPS_MORPHEUS_GSC_RECOV) {
+			DPRINTF(IPS_D_ERR, ("%s: intr, error 0x%x",
+			    sc->sc_dev.dv_xname, gsc));
+			DPRINTF(IPS_D_ERR, (", OISR 0x%08x, OQPR 0x%08x\n",
+			    oisr, oqpr));
+		}
+
+		if (id >=3D sc->sc_ai.max_concurrent_cmds) {
+			DPRINTF(IPS_D_ERR, ("%s: intr, bogus id %d",
+			    sc->sc_dev.dv_xname, id));
+			DPRINTF(IPS_D_ERR, (", OISR 0x%08x, OQPR 0x%08x\n",
+			    oisr, oqpr));
+			continue;
+		}
+
+		ccb =3D &sc->sc_ccb[id];
+		if (!(ccb->c_flags & CCB_F_RUN)) {
+			DPRINTF(IPS_D_ERR, ("%s: intr, ccb id %d not run",
+			    sc->sc_dev.dv_xname, id));
+			DPRINTF(IPS_D_ERR, (", OISR 0x%08x, OQPR 0x%08x\n",
+			    oisr, oqpr));
+			continue;
+		}
+
+		rv =3D 1;
+		bus_dmamap_unload(sc->sc_dmat, ccb->c_dmam);
+		xs =3D ccb->c_xfer;
+		xs->resid =3D 0;
+		xs->xs_status |=3D XS_STS_DONE;
+		callout_stop(&xs->xs_callout);
+		s =3D splbio();
+		scsipi_done(xs);
+		ccb->c_flags &=3D ~CCB_F_RUN;
+		TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, c_link);
+		splx(s);
+	}
+
+	return (rv);
+}
+
+struct ccb *
+ips_ccb_alloc(bus_dma_tag_t dmat, int n)
+{
+	struct ccb *ccb;
+	int i;
+
+	if ((ccb =3D malloc(n * sizeof(*ccb), M_DEVBUF, M_NOWAIT)) =3D=3D NULL)
+		return (NULL);
+	bzero(ccb, n * sizeof(*ccb));
+
+	for (i =3D 0; i < n; i++) {
+		ccb[i].c_id =3D i;
+		if (bus_dmamap_create(dmat, IPS_MAXFER, IPS_MAXSGS,
+		    IPS_MAXFER, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+		    &ccb[i].c_dmam))
+			goto fail;
+	}
+
+	return (ccb);
+fail:
+	for (; i > 0; i--)
+		bus_dmamap_destroy(dmat, ccb[i - 1].c_dmam);
+	free(ccb, M_DEVBUF);
+	return (NULL);
+}
+
+void
+ips_ccb_free(struct ccb *ccb, bus_dma_tag_t dmat, int n)
+{
+	int i;
+
+	for (i =3D 0; i < n; i++)
+		bus_dmamap_destroy(dmat, ccb[i - 1].c_dmam);
+	free(ccb, M_DEVBUF);
+}
+
+struct dmamem *
+ips_dmamem_alloc(bus_dma_tag_t tag, bus_size_t size)
+{
+	struct dmamem *dm;
+	int nsegs;
+
+	if ((dm =3D malloc(sizeof(*dm), M_DEVBUF, M_NOWAIT)) =3D=3D NULL)
+		return (NULL);
+
+	dm->dm_tag =3D tag;
+	dm->dm_size =3D size;
+
+	if (bus_dmamap_create(tag, size, 1, size, 0,
+	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &dm->dm_map))
+		goto fail1;
+	if (bus_dmamem_alloc(tag, size, 0, 0, &dm->dm_seg, 1, &nsegs,
+	    BUS_DMA_NOWAIT))
+		goto fail2;
+	if (bus_dmamem_map(tag, &dm->dm_seg, 1, size, (caddr_t *)&dm->dm_kva,
+	    BUS_DMA_NOWAIT))
+		goto fail3;
+	bzero(dm->dm_kva, size);
+	if (bus_dmamap_load(tag, dm->dm_map, dm->dm_kva, size, NULL,
+	    BUS_DMA_NOWAIT))
+		goto fail4;
+
+	return (dm);
+
+fail4:
+	bus_dmamem_unmap(tag, dm->dm_kva, size);
+fail3:
+	bus_dmamem_free(tag, &dm->dm_seg, 1);
+fail2:
+	bus_dmamap_destroy(tag, dm->dm_map);
+fail1:
+	free(dm, M_DEVBUF);
+	return (NULL);
+}
+
+void
+ips_dmamem_free(struct dmamem *dm)
+{
+	bus_dmamap_unload(dm->dm_tag, dm->dm_map);
+	bus_dmamem_unmap(dm->dm_tag, dm->dm_kva, dm->dm_size);
+	bus_dmamem_free(dm->dm_tag, &dm->dm_seg, 1);
+	bus_dmamap_destroy(dm->dm_tag, dm->dm_map);
+	free(dm, M_DEVBUF);
+}
Index: dev/pci/pcidevs
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/dev/pci/pcidevs,v
retrieving revision 1.855.2.4
diff -u -r1.855.2.4 pcidevs
--- dev/pci/pcidevs	21 Feb 2007 13:17:59 -0000	1.855.2.4
+++ dev/pci/pcidevs	15 May 2007 02:00:26 -0000
@@ -820,6 +820,8 @@
 /* XXX guess */
 product ADP2 PERC_3QC		0x1365	Dell PERC 3/QC
=20
+product	ADP2 SERVERAID		0x0250	ADP2 ServeRAID
+
 /* Addtron Products */
 product ADDTRON 8139	0x1360	8139 Ethernet
 product ADDTRON RHINEII	0x1320	Rhine II 10/100 Ethernet
Index: dev/pci/pcidevs.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/dev/pci/pcidevs.h,v
retrieving revision 1.854.2.4
diff -u -r1.854.2.4 pcidevs.h
--- dev/pci/pcidevs.h	21 Feb 2007 13:18:47 -0000	1.854.2.4
+++ dev/pci/pcidevs.h	15 May 2007 02:00:27 -0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: pcidevs.h,v 1.854.2.4 2007/02/21 13:18:47 tron Exp $	*/
+/*	$NetBSD$	*/
=20
 /*
  * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.
@@ -827,6 +827,8 @@
 /* XXX guess */
 #define	PCI_PRODUCT_ADP2_PERC_3QC	0x1365		/* Dell PERC 3/QC */
=20
+#define	PCI_PRODUCT_ADP2_SERVERAID	0x0250		/* ADP2 ServeRAID */
+
 /* Addtron Products */
 #define	PCI_PRODUCT_ADDTRON_8139	0x1360		/* 8139 Ethernet */
 #define	PCI_PRODUCT_ADDTRON_RHINEII	0x1320		/* Rhine II 10/100 Ethernet */
Index: dev/pci/pcidevs_data.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/dev/pci/pcidevs_data.h,v
retrieving revision 1.853.2.4
diff -u -r1.853.2.4 pcidevs_data.h
--- dev/pci/pcidevs_data.h	21 Feb 2007 13:18:48 -0000	1.853.2.4
+++ dev/pci/pcidevs_data.h	15 May 2007 02:00:28 -0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: pcidevs_data.h,v 1.853.2.4 2007/02/21 13:18:48 tron Exp $	*/
+/*	$NetBSD$	*/
=20
 /*
  * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.
@@ -3008,6 +3008,10 @@
 	    "Dell PERC 3/QC",
 	},
 	{
+	    PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_SERVERAID,
+	    "ADP2 ServeRAID",
+	},
+	{
 	    PCI_VENDOR_ADDTRON, PCI_PRODUCT_ADDTRON_8139,
 	    "8139 Ethernet",
 	},
@@ -12708,4 +12712,4 @@
 	    "Video Controller",
 	},
 };
-const int pci_nproducts =3D 2583;
+const int pci_nproducts =3D 2584;
Index: dev/scsipi/scsipi_all.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/sys/dev/scsipi/scsipi_all.h,v
retrieving revision 1.32
diff -u -r1.32 scsipi_all.h
--- dev/scsipi/scsipi_all.h	1 Dec 2006 21:36:56 -0000	1.32
+++ dev/scsipi/scsipi_all.h	15 May 2007 02:00:28 -0000
@@ -55,6 +55,8 @@
  * Some basic, common SCSI commands
  */
=20
+#define	TEST_UNIT_READY		0x00
+#define	REQUEST_SENSE		0x03
 #define	INQUIRY			0x12
 struct scsipi_inquiry {
 	u_int8_t opcode;

--WIyZ46R2i8wDzkSu--

--rJwd6BRFiFCcLxzm
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (NetBSD)

iQIVAwUBRkoIQe1mMGan/TnWAQJ1ig//R2RC4PBPILSK215RxTXXRZFY7C/ybxFV
Zosby6Z6ttzk/x+jiwGFl0WuALfJvTt7XUHuFNmx7kvybjf6V26lpcJxnsxnb6qW
mWC5XF8LuLj5MctNbpfAo+ip4/lE5iHrWvN4DXGlKb4bXwDT7Rew9J0AaMcHRwTN
YpIq2lLh3n1hVMlK4P2hHlm3KGFD9r4yPusW3HGcmIznVxYLHvV6P01Wv476OfBm
1hBrO+1W+WR92drKznQPcAtmwNpUlTqnvcStFRDpEYfuIPJrlWe5s3DtZHY7vzyf
tXgGvLJqbmGo27lVMnKIS0XVtkFImlQj/WnKxIhijpo7vgmsshOeiWYIrCpQMKfo
F3GtaSlvovVs9e4D6eQ00tSQ8t4vwQxoXdAc4aq8TwjVF4RoZ3zhdHP4qiN8yiUY
qE03XV/4pha1wIQooKuuwK6dC3i9LTQIsuSdeoBQAxQzj3OIx8DxJQof8NVsO+ao
Fm3dIFgpZRWyRAl8EsNH+uM4el2vi+uTti0JdikvT7e+z3jkR//2CNgsgeYJRZ6H
rgGby3TAA03K+u8sc+QRRrwFTaBID9P11GFtmCMGKgh76fgbNWG14o8auQALVJC3
K1l7wybEy93UCjyiXcqFZmqZRLkufOaUVdxh0mSzE1EKnFYaBsUPSpaCPejpaUSW
WZdM+x/eq4Q=
=DDkf
-----END PGP SIGNATURE-----

--rJwd6BRFiFCcLxzm--