PSARC 2007/654 blk2scsa
authorGarrett D'Amore <gdamore@opensolaris.org>
Fri, 08 Aug 2008 11:50:24 -0700
changeset 7302 d9c4046525ac
parent 7301 fc54ebb42916
child 7303 2a37233e2030
PSARC 2007/654 blk2scsa PSARC 2007/659 SDcard Stack Phase I PSARC 2008/001 SDcard Simplified Synchronous Command Processing 6632236 blk2scsa generic block layer 6632237 SDcard stack phase I 6649849 desire winbond sdcard reader support
usr/src/Makefile.lint
usr/src/cmd/devfsadm/cfg_link.c
usr/src/cmd/devfsadm/cfg_link.h
usr/src/lib/cfgadm_plugins/Makefile
usr/src/lib/cfgadm_plugins/sdcard/Makefile
usr/src/lib/cfgadm_plugins/sdcard/Makefile.com
usr/src/lib/cfgadm_plugins/sdcard/amd64/Makefile
usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.c
usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.h
usr/src/lib/cfgadm_plugins/sdcard/common/mapfile-vers
usr/src/lib/cfgadm_plugins/sdcard/i386/Makefile
usr/src/lib/cfgadm_plugins/sdcard/sdcard.xcl
usr/src/lib/cfgadm_plugins/sdcard/sparc/Makefile
usr/src/lib/cfgadm_plugins/sdcard/sparcv9/Makefile
usr/src/pkgdefs/Makefile
usr/src/pkgdefs/SUNWckr/prototype_i386
usr/src/pkgdefs/SUNWckr/prototype_sparc
usr/src/pkgdefs/SUNWcsl/prototype_com
usr/src/pkgdefs/SUNWcsl/prototype_i386
usr/src/pkgdefs/SUNWcsl/prototype_sparc
usr/src/pkgdefs/SUNWsdcard/Makefile
usr/src/pkgdefs/SUNWsdcard/pkginfo.tmpl
usr/src/pkgdefs/SUNWsdcard/postinstall
usr/src/pkgdefs/SUNWsdcard/postremove
usr/src/pkgdefs/SUNWsdcard/prototype_com
usr/src/pkgdefs/SUNWsdcard/prototype_i386
usr/src/pkgdefs/SUNWsdcard/prototype_sparc
usr/src/pkgdefs/SUNWwbsd/Makefile
usr/src/pkgdefs/SUNWwbsd/depend
usr/src/pkgdefs/SUNWwbsd/pkginfo.tmpl
usr/src/pkgdefs/SUNWwbsd/postinstall
usr/src/pkgdefs/SUNWwbsd/postremove
usr/src/pkgdefs/SUNWwbsd/prototype_com
usr/src/pkgdefs/SUNWwbsd/prototype_i386
usr/src/pkgdefs/SUNWwbsd/prototype_sparc
usr/src/pkgdefs/etc/exception_list_i386
usr/src/pkgdefs/etc/exception_list_sparc
usr/src/uts/common/Makefile.files
usr/src/uts/common/Makefile.rules
usr/src/uts/common/io/scsi/adapters/blk2scsa/blk2scsa.c
usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c
usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.h
usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c
usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.h
usr/src/uts/common/io/sdcard/impl/mapfile
usr/src/uts/common/io/sdcard/impl/sda_cmd.c
usr/src/uts/common/io/sdcard/impl/sda_host.c
usr/src/uts/common/io/sdcard/impl/sda_init.c
usr/src/uts/common/io/sdcard/impl/sda_mem.c
usr/src/uts/common/io/sdcard/impl/sda_mod.c
usr/src/uts/common/io/sdcard/impl/sda_nexus.c
usr/src/uts/common/io/sdcard/impl/sda_slot.c
usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c
usr/src/uts/common/io/warlock/blk2scsa.wlcmd
usr/src/uts/common/io/warlock/sdhost.wlcmd
usr/src/uts/common/sys/Makefile
usr/src/uts/common/sys/Makefile.syshdrs
usr/src/uts/common/sys/scsi/adapters/blk2scsa.h
usr/src/uts/common/sys/sdcard/sda.h
usr/src/uts/common/sys/sdcard/sda_impl.h
usr/src/uts/common/sys/sdcard/sda_ioctl.h
usr/src/uts/common/sys/sunddi.h
usr/src/uts/intel/Makefile.intel.shared
usr/src/uts/intel/blk2scsa/Makefile
usr/src/uts/intel/sda/Makefile
usr/src/uts/intel/sdcard/Makefile
usr/src/uts/intel/sdhost/Makefile
usr/src/uts/intel/warlock/Makefile
usr/src/uts/sparc/Makefile.sparc.shared
usr/src/uts/sparc/blk2scsa/Makefile
usr/src/uts/sparc/sda/Makefile
usr/src/uts/sparc/sdcard/Makefile
usr/src/uts/sparc/sdhost/Makefile
usr/src/uts/sparc/warlock/Makefile
usr/src/uts/sparc/wbsd/Makefile
--- a/usr/src/Makefile.lint	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/Makefile.lint	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 # include global definitions
 include Makefile.master
@@ -305,6 +303,7 @@
 	lib/auditd_plugins \
 	lib/brand/native \
 	lib/brand/sn1 \
+	lib/cfgadm_plugins/sdcard \
 	lib/crypt_modules \
 	lib/extendedFILE \
 	lib/libadt_jni \
--- a/usr/src/cmd/devfsadm/cfg_link.c	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/cmd/devfsadm/cfg_link.c	Fri Aug 08 11:50:24 2008 -0700
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <devfsadm.h>
 #include <stdio.h>
 #include <strings.h>
@@ -59,6 +57,7 @@
 static int	pci_cfg_creat_cb(di_minor_t minor, di_node_t node);
 static int	ib_cfg_creat_cb(di_minor_t minor, di_node_t node);
 static int	sata_cfg_creat_cb(di_minor_t minor, di_node_t node);
+static int	sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node);
 
 static di_node_t	pci_cfg_chassis_node(di_node_t, di_prom_handle_t);
 static char 	*pci_cfg_slotname(di_node_t, di_prom_handle_t, minor_t);
@@ -117,6 +116,9 @@
 	},
 	{ "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL,
 	    TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb
+	},
+	{ "attachment-point", DDI_NT_SDCARD_ATTACHMENT_POINT, NULL,
+	    TYPE_EXACT, ILEVEL_0, sdcard_cfg_creat_cb
 	}
 };
 
@@ -146,7 +148,10 @@
 	},
 	{ "attachment-point", SATA_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
 	    ILEVEL_0, devfsadm_rm_all
-	}
+	},
+	{ "attachment-point", SDCARD_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
+	    ILEVEL_0, devfsadm_rm_all
+	},
 };
 
 DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt);
@@ -264,7 +269,7 @@
 	}
 
 	(void) snprintf(l_path, sizeof (l_path), "%s/sata%s/%s", CFG_DIRNAME,
-			buf, minor_nm);
+	    buf, minor_nm);
 	free(buf);
 
 	(void) devfsadm_mklink(l_path, node, minor, 0);
@@ -272,6 +277,39 @@
 	return (DEVFSADM_CONTINUE);
 }
 
+static int
+sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node)
+{
+	char path[PATH_MAX +1], l_path[PATH_MAX], *buf, *devfspath;
+	char *minor_nm;
+	devfsadm_enumerate_t rules[1] =
+	    {"^cfg$/^sdcard([0-9]+)$", 1, MATCH_ADDR};
+
+	minor_nm = di_minor_name(minor);
+	if (minor_nm == NULL)
+		return (DEVFSADM_CONTINUE);
+
+	devfspath = di_devfs_path(node);
+	if (devfspath == NULL)
+		return (DEVFSADM_CONTINUE);
+
+	(void) snprintf(path, sizeof (path), "%s:%s", devfspath, minor_nm);
+	di_devfs_path_free(devfspath);
+
+	/* build the physical path from the components */
+	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) ==
+	    DEVFSADM_FAILURE) {
+		return (DEVFSADM_CONTINUE);
+	}
+
+	(void) snprintf(l_path, sizeof (l_path), "%s/sdcard%s/%s",
+	    CFG_DIRNAME, buf, minor_nm);
+	free(buf);
+
+	(void) devfsadm_mklink(l_path, node, minor, 0);
+
+	return (DEVFSADM_CONTINUE);
+}
 
 /*
  * get_roothub:
@@ -557,7 +595,7 @@
 	serid = (uint64_t)*seridp;
 
 	if ((serid >> 40) != (uint64_t)IEEE_SUN_ID ||
-		!serid_printable(&serid)) {
+	    !serid_printable(&serid)) {
 		(void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid);
 		return (1);
 	}
--- a/usr/src/cmd/devfsadm/cfg_link.h	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/cmd/devfsadm/cfg_link.h	Fri Aug 08 11:50:24 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -28,8 +28,6 @@
 #ifndef	_CFG_LINK_H
 #define	_CFG_LINK_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <devfsadm.h>
 
 #ifdef	__cplusplus
@@ -42,6 +40,7 @@
 #define	PCI_CFG_LINK_RE		"^cfg/[:alnum:]$"
 #define	IB_CFG_LINK_RE		"^cfg/(hca[0-9A-F]+)$"
 #define	SATA_CFG_LINK_RE	"^cfg/((sata[0-9]+)/([0-9]+)([.]([0-9])+)*)$"
+#define	SDCARD_CFG_LINK_RE	"^cfg/sdcard[0-9]+/[0-9]+$"
 #define	PCI_CFG_PATH_LINK_RE	\
 	"^cfg/(.*(pci[0-9]|pcie[0-9]|Slot[0-9]|\\<pci\\>|\\<pcie\\>).*)$"
 
--- a/usr/src/lib/cfgadm_plugins/Makefile	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/lib/cfgadm_plugins/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -19,17 +19,15 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # lib/cfgadm_plugins/Makefile
 #
 
 include $(SRC)/Makefile.master
 
-COMMON_SUBDIRS= scsi pci usb ib
+COMMON_SUBDIRS= scsi sdcard pci usb ib
 sparc_SUBDIRS=	sbd ac sysctrl
 
 i386_SUBDIRS= sata
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,70 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include		../../Makefile.lib
+
+SUBDIRS =	$(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all :=		TARGET= all
+clean :=	TARGET= clean
+clobber :=	TARGET= clobber
+delete :=	TARGET= delete
+install :=	TARGET= install
+lint := 	TARGET= lint
+_msg :=		TARGET= _msg
+package := 	TARGET= package
+
+TEXT_DOMAIN= 	SUNW_OST_OSLIB
+XGETFLAGS=	-a -x sdcard.xcl
+POFILE=		sdcard.po
+POFILES=	generic.po
+
+SED=	sed
+GREP=	grep
+CP=	cp
+
+.KEEP_STATE:
+
+all clean clobber delete install lint package: $(SUBDIRS)
+
+$(SUBDIRS):	FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg:	$(MSGDOMAIN) $(POFILE)
+	$(RM) $(MSGDOMAIN)/$(POFILE)
+	$(CP) $(POFILE) $(MSGDOMAIN)
+
+$(POFILE):	$(POFILES)
+	$(RM) $@
+	$(CAT) $(POFILES) > $@
+
+$(POFILES):
+	$(RM) messages.po
+	$(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext */*.[ch]`
+	$(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+	$(RM) messages.po
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/Makefile.com	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,70 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY= sdcard.a
+VERS= .1
+
+OBJECTS= cfga_sdcard.o
+
+# include library definitions
+include ../../../Makefile.lib
+
+SRCDIR =	../common
+ROOTLIBDIR=	$(ROOT)/usr/lib/cfgadm
+ROOTLIBDIR64=	$(ROOTLIBDIR)/$(MACH64)
+
+LIBS=	$(DYNLIB)
+
+LINTFLAGS +=	-DDEBUG
+LINTFLAGS64 +=	-DDEBUG
+
+CFLAGS +=	$(CCVERBOSE)
+CFLAGS64 +=	$(CCVERBOSE)
+
+LDLIBS +=	-lc -ldevice -ldevinfo -lrcm
+
+.KEEP_STATE:
+
+all:	$(LIBS)
+
+lint:	lintcheck
+
+# Install rules
+
+$(ROOTLIBDIR)/%: % $(ROOTLIBDIR)
+	$(INS.file)
+
+$(ROOTLIBDIR64)/%: % $(ROOTLIBDIR64)
+	$(INS.file)
+
+$(ROOTLIBDIR) $(ROOTLIBDIR64):
+	$(INS.dir)
+
+# include library targets
+include ../../../Makefile.targ
+
+objs/%.o pics/%.o: ../common/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/amd64/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+CFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+LINTFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,1933 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include "cfga_sdcard.h"
+
+/*
+ * This file contains the entry points to the plug-in as defined in the
+ * config_admin(3X) man page.
+ */
+
+/*
+ * Set the version number for the cfgadm library's use.
+ */
+int cfga_version = CFGA_HSL_V2;
+
+enum {
+	HELP_HEADER = 1,
+	HELP_CONFIG,
+	HELP_RESET_SLOT,
+	HELP_UNKNOWN
+};
+
+/* SDCARD specific help messages */
+static char *sdcard_help[] = {
+	NULL,
+	"SD card specific commands:\n",
+	" cfgadm -c [configure|unconfigure|disconnect|connect] ap_id "
+	"[ap_id...]\n",
+	" cfgadm -x sdcard_reset_slot ap_id [ap_id...]\n",
+	"\tunknown command or option:\n",
+	NULL
+};	/* End help messages */
+
+
+/*
+ * Messages.
+ */
+static msgcvt_t sdcard_msgs[] = {
+	/* CFGA_SDCARD_OK */
+	{ CVT, CFGA_OK, "" },
+
+	/* CFGA_SDCARD_NACK */
+	{ CVT, CFGA_NACK, "" },
+
+	/* CFGA_SDCARD_UNKNOWN / CFGA_LIB_ERROR -> "Library error" */
+	{ CVT, CFGA_LIB_ERROR, "Unknown message; internal error" },
+
+	/* CFGA_SDCARD_PRIV / CFGA_PRIV -> "Insufficient privileges" */
+	{ CVT, CFGA_PRIV, "" },
+
+	/*
+	 * CFGA_SDCARD_DYNAMIC_AP /
+	 * CFGA_LIB_ERROR -> "Configuration operation invalid"
+	 */
+	{ CVT, CFGA_INVAL, "Cannot identify attached device" },
+
+	/* CFGA_SDCARD_INTERNAL_ERROR / CFGA_LIB_ERROR -> "Library error" */
+	{ CVT, CFGA_LIB_ERROR, "Internal error" },
+
+	/* CFGA_SDCARD_ALLOC_FAIL / CFGA_LIB_ERROR -> "Library error" */
+	{ CVT, CFGA_LIB_ERROR, "Memory allocation failure" },
+
+	/* CFGA_SDCARD_IOCTL / CFGA_ERROR -> "Hardware specific failure"  */
+	{ CVT, CFGA_ERROR, "Driver ioctl failed " },
+
+	/* CFGA_SDCARD_DEVCTL / CFGA_LIB_ERROR -> "Library error" */
+	{ CVT, CFGA_LIB_ERROR, "Internal error: "
+	    "Cannot allocate devctl handle " },
+
+	/* CFGA_SDCARD_AP / CFGA_APID_NOEXIST -> "Attachment point not found" */
+	{ CVT, CFGA_APID_NOEXIST, "" },
+
+	/*
+	 * CFGA_SDCARD_BUSY /
+	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
+	 */
+	{ CVT, CFGA_SYSTEM_BUSY, "" },
+
+	/* CFGA_SDCARD_DEVLINK / CFGA_LIB_ERROR -> "Library error" */
+	{ CVT, CFGA_LIB_ERROR, "Could not find /dev/cfg link for " },
+
+	/*
+	 * CFGA_SDCARD_INVALID_DEVNAME /
+	 * CFGA_INVAL -> "Configuration operation invalid"
+	 */
+	{ CVT, CFGA_INVAL, "Cannot specify device name" },
+
+	/* CFGA_SDCARD_DATA_ERROR / CFGA_DATA_ERROR -> "Data error" */
+	{ CVT, CFGA_DATA_ERROR, "cfgadm data error" },
+
+	/*
+	 * CFGA_SDCARD_DEV_CONFIGURE /
+	 * CFGA_ERROR -> "Hardware specific failure"
+	 */
+	{ CVT, CFGA_ERROR, "Failed to config device at " },
+
+	/*
+	 * CFGA_SDCARD_DEV_UNCONFIGURE /
+	 * CFGA_ERROR -> "Hardware specific failure"
+	 */
+	{ CVT, CFGA_ERROR, "Failed to unconfig device at " },
+
+	/*
+	 * CFGA_SDCARD_NOT_CONNECTED
+	 * CFGA_INVAL -> "Configuration operation invalid"
+	 */
+	{ CVT, CFGA_INVAL, "No device connected to " },
+
+	/*
+	 * CFGA_SDCARD_DISCONNECTED
+	 * CFGA_INVAL -> "Configuration operation invalid"
+	 */
+	{ CVT, CFGA_INVAL, "Slot already disconnected at " },
+
+	/*
+	 * CFGA_SDCARD_NOT_CONFIGURED /
+	 * CFGA_INVAL -> "Configuration operation invalid"
+	 */
+	{ CVT, CFGA_INVAL, "No device configured at " },
+
+	/*
+	 * CFGA_SDCARD_ALREADY_CONNECTED /
+	 * CFGA_INVAL -> "Configuration operation invalid"
+	 */
+	{ CVT, CFGA_INVAL, "Device already connected to " },
+
+	/*
+	 * CFGA_SDCARD_ALREADY_CONFIGURED /
+	 * CFGA_INVAL -> "Configuration operation invalid"
+	 */
+	{ CVT, CFGA_INVAL, "Device already configured at " },
+
+	/* CFGA_SDCARD_DEVICE_UNCONFIGURED */
+	{ CVT, CFGA_OK, "Device unconfigured prior to disconnect" },
+
+	/*
+	 * CFGA_SDCARD_OPNOTSUPP /
+	 * CFGA_OPNOTSUPP -> "Configuration operation not supported"
+	 */
+	{ CVT, CFGA_OPNOTSUPP, "Operation not supported" },
+
+	/*
+	 * CFGA_SDCARD_HWOPNOTSUPP /
+	 * CFGA_ERROR -> "Hardware specific failure"
+	 */
+	{ CVT, CFGA_ERROR, "Hardware specific operation not supported" },
+
+	/* CFGA_SDCARD_OPTIONS / CFGA_ERROR -> "Hardware specific failure" */
+	{ CVT, CFGA_ERROR, "Hardware specific option not supported" },
+
+	/* CFGA_SDCARD_STATE / CFGA_LIB_ERROR -> "Library error" */
+	{ CVT, CFGA_LIB_ERROR, "Internal error: Unrecognized ap state" },
+
+	/* CFGA_SDCARD_OPEN / CFGA_LIB_ERROR -> "Library error" */
+	{ CVT, CFGA_LIB_ERROR, "Cannot open " },
+
+	/*
+	 * CFGA_SDCARD_RCM_HANDLE /
+	 * CFGA_ERROR -> "Hardware specific failure"
+	 */
+	{ CVT, CFGA_ERROR, "cannot get RCM handle"},
+
+	/*
+	 * CFGA_SDCARD_RCM_OFFLINE /
+	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
+	 */
+	{ CVT, CFGA_SYSTEM_BUSY, "failed to offline: "},
+
+	/*
+	 * CFGA_SDCARD_RCM_REMOVE /
+	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
+	 */
+	{ CVT, CFGA_SYSTEM_BUSY, "failed to remove: "},
+
+	/*
+	 * CFGA_SDCARD_RCM_ONLINE /
+	 * CFGA_SYSTEM_BUSY -> "System is busy, try again"
+	 */
+	{ CVT, CFGA_ERROR, "failed to online: "},
+
+	/* CFGA_SDCARD_CONFIRM_RESET */
+	{ CVT, CFGA_OK, "Reset the device at %s?\n"
+	    "This will operation will disrupt activity on the SD card.\n"
+	    "Continue"
+	},
+
+	/* CFGA_SDCARD_CONFIRM_UNCONFIGURE */
+	{ CVT, CFGA_OK, "Unconfigure the device at %s?\n"
+	    "This will operation will disrupt activity on the SD card.\n"
+	    "Continue"
+	},
+
+	/* CFGA_SDCARD_CONFIRM_DISCONNECT */
+	{ CVT, CFGA_OK, "Disconnect the device at %s?\n"
+	    "This will operation will disrupt activity on the SD card.\n"
+	    "Continue"
+	}
+};
+
+static cfga_err_t
+sdcard_err_msg(char **errstring, cfga_sdcard_ret_t ret, const char *, int);
+
+static cfga_sdcard_ret_t
+verify_params(const char *ap_id, const char *options, char **errstring);
+
+static cfga_sdcard_ret_t
+setup_for_devctl_cmd(const char *ap_id, devctl_hdl_t *devctl_hdl, uint_t oflag);
+
+static cfga_sdcard_ret_t
+slot_state(devctl_hdl_t hdl, ap_rstate_t *rstate, ap_ostate_t *ostate);
+
+static cfga_sdcard_ret_t
+do_control_ioctl(const char *ap_id, int subcommand, void *data, size_t size);
+
+static void
+cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl);
+
+static cfga_sdcard_ret_t
+sdcard_get_devicepath(const char *ap_id, char *devpath);
+
+static cfga_sdcard_ret_t
+sdcard_reset_slot(const char *ap_id);
+
+static int
+sdcard_confirm(struct cfga_confirm *confp, char *msg);
+
+static cfga_sdcard_ret_t
+sdcard_rcm_offline(char *, char **, cfga_flags_t);
+
+static void
+sdcard_rcm_online(char *, char **);
+
+static void
+sdcard_rcm_remove(char *, char **);
+
+static void
+sdcard_rcm_info_table(rcm_info_t *, char **);
+
+static cfga_sdcard_ret_t
+sdcard_rcm_init(void);
+
+
+
+/* Utilities */
+
+static cfga_sdcard_ret_t
+physpath_to_devlink(const char *basedir, const char *node_path,
+    char **logpp, int *l_errnop)
+{
+	char *linkpath;
+	char *buf;
+	char *real_path;
+	DIR *dp;
+	struct dirent *dep, *newdep;
+	int deplen;
+	boolean_t found = B_FALSE;
+	int err = 0;
+	struct stat sb;
+	char *p;
+	cfga_sdcard_ret_t rv = CFGA_SDCARD_INTERNAL_ERROR;
+
+	/*
+	 * Using libdevinfo for this is overkill and kills performance
+	 * when multiple consumers of libcfgadm are executing
+	 * concurrently.
+	 */
+	if ((dp = opendir(basedir)) == NULL) {
+		*l_errnop = errno;
+		return (CFGA_SDCARD_INTERNAL_ERROR);
+	}
+
+	linkpath = malloc(PATH_MAX);
+	buf = malloc(PATH_MAX);
+	real_path = malloc(PATH_MAX);
+
+	deplen = pathconf(basedir, _PC_NAME_MAX);
+	deplen = (deplen <= 0 ? MAXNAMELEN : deplen) +
+	    sizeof (struct dirent);
+	dep = (struct dirent *)malloc(deplen);
+
+	if (dep == NULL || linkpath == NULL || buf == NULL ||
+	    real_path == NULL) {
+		*l_errnop = ENOMEM;
+		rv = CFGA_SDCARD_ALLOC_FAIL;
+		goto pp_cleanup;
+	}
+
+	*logpp = NULL;
+
+	while (!found && (err = readdir_r(dp, dep, &newdep)) == 0 &&
+	    newdep != NULL) {
+
+		assert(newdep == dep);
+
+		if (strcmp(dep->d_name, ".") == 0 ||
+		    strcmp(dep->d_name, "..") == 0)
+			continue;
+
+		(void) snprintf(linkpath, MAXPATHLEN,
+		    "%s/%s", basedir, dep->d_name);
+
+		if (lstat(linkpath, &sb) < 0)
+			continue;
+
+		if (S_ISDIR(sb.st_mode)) {
+
+			if ((rv = physpath_to_devlink(linkpath, node_path,
+			    logpp, l_errnop)) != CFGA_SDCARD_OK) {
+
+				goto pp_cleanup;
+			}
+
+			if (*logpp != NULL)
+				found = B_TRUE;
+
+		} else if (S_ISLNK(sb.st_mode)) {
+
+			bzero(buf, PATH_MAX);
+			if (readlink(linkpath, buf, PATH_MAX) < 0)
+				continue;
+
+
+			/*
+			 * realpath() is too darn slow, so fake
+			 * it, by using what we know about /dev
+			 * links: they are always of the form:
+			 * <"../">+/devices/<path>
+			 */
+			p = buf;
+			while (strncmp(p, "../", 3) == 0)
+				p += 3;
+
+			if (p != buf)
+				p--;	/* back up to get a slash */
+
+			assert (*p == '/');
+
+			if (strcmp(p, node_path) == 0) {
+				*logpp = strdup(linkpath);
+				if (*logpp == NULL) {
+
+					rv = CFGA_SDCARD_ALLOC_FAIL;
+					goto pp_cleanup;
+				}
+
+				found = B_TRUE;
+			}
+		}
+	}
+
+	free(linkpath);
+	free(buf);
+	free(real_path);
+	free(dep);
+	(void) closedir(dp);
+
+	if (err != 0) {
+		*l_errnop = err;
+		return (CFGA_SDCARD_INTERNAL_ERROR);
+	}
+
+	return (CFGA_SDCARD_OK);
+
+pp_cleanup:
+
+	if (dp)
+		(void) closedir(dp);
+	if (dep)
+		free(dep);
+	if (linkpath)
+		free(linkpath);
+	if (buf)
+		free(buf);
+	if (real_path)
+		free(real_path);
+	if (*logpp) {
+		free(*logpp);
+		*logpp = NULL;
+	}
+	return (rv);
+}
+
+
+/*
+ * Given the index into a table (msgcvt_t) of messages, get the message
+ * string, converting it to the proper locale if necessary.
+ * NOTE: Indexes are defined in cfga_sdcard.h
+ */
+static const char *
+get_msg(uint_t msg_index, msgcvt_t *msg_tbl, uint_t tbl_size)
+{
+	if (msg_index >= tbl_size) {
+		msg_index = CFGA_SDCARD_UNKNOWN;
+	}
+
+	return ((msg_tbl[msg_index].intl) ?
+	    dgettext(TEXT_DOMAIN, msg_tbl[msg_index].msgstr) :
+	    msg_tbl[msg_index].msgstr);
+}
+
+/*
+ * Allocates and creates a message string (in *ret_str),
+ * by concatenating all the (char *) args together, in order.
+ * Last arg MUST be NULL.
+ */
+static void
+set_msg(char **ret_str, ...)
+{
+	char    *str;
+	size_t  total_len;
+	va_list valist;
+
+	va_start(valist, ret_str);
+
+	total_len = (*ret_str == NULL) ? 0 : strlen(*ret_str);
+
+	while ((str = va_arg(valist, char *)) != NULL) {
+		size_t  len = strlen(str);
+		char    *old_str = *ret_str;
+
+		*ret_str = (char *)realloc(*ret_str, total_len + len + 1);
+		if (*ret_str == NULL) {
+			/* We're screwed */
+			free(old_str);
+			va_end(valist);
+			return;
+		}
+
+		(void) strcpy(*ret_str + total_len, str);
+		total_len += len;
+	}
+
+	va_end(valist);
+}
+
+/*
+ * Error message handling.
+ * For the rv passed in, looks up the corresponding error message string(s),
+ * internationalized if necessary, and concatenates it into a new
+ * memory buffer, and points *errstring to it.
+ * Note not all rvs will result in an error message return, as not all
+ * error conditions warrant an SD-specific error message - for those
+ * conditions the cfgadm generic messages are sufficient.
+ *
+ * Some messages may display ap_id or errno, which is why they are passed
+ * in.
+ */
+cfga_err_t
+sdcard_err_msg(
+	char **errstring,
+	cfga_sdcard_ret_t rv,
+	const char *ap_id,
+	int l_errno)
+{
+	if (errstring == NULL) {
+		return (sdcard_msgs[rv].cfga_err);
+	}
+
+	/*
+	 * Generate the appropriate SDCARD-specific error message(s) (if any).
+	 */
+	switch (rv) {
+	case CFGA_SDCARD_OK:
+	case CFGA_NACK:
+		/* Special case - do nothing.  */
+		break;
+
+	case CFGA_SDCARD_UNKNOWN:
+	case CFGA_SDCARD_PRIV:
+	case CFGA_SDCARD_DYNAMIC_AP:
+	case CFGA_SDCARD_INTERNAL_ERROR:
+	case CFGA_SDCARD_ALLOC_FAIL:
+	case CFGA_SDCARD_DATA_ERROR:
+	case CFGA_SDCARD_OPNOTSUPP:
+	case CFGA_SDCARD_OPTIONS:
+	case CFGA_SDCARD_STATE:
+
+		/* These messages require no additional strings passed. */
+		set_msg(errstring, ERR_STR(rv), NULL);
+		break;
+
+	case CFGA_SDCARD_HWOPNOTSUPP:
+		/* hardware-specific help needed */
+		set_msg(errstring, ERR_STR(rv), NULL);
+		set_msg(errstring, "\n",
+		    dgettext(TEXT_DOMAIN, sdcard_help[HELP_HEADER]), NULL);
+		set_msg(errstring, sdcard_help[HELP_RESET_SLOT], NULL);
+		break;
+
+	case CFGA_SDCARD_AP:
+	case CFGA_SDCARD_BUSY:
+	case CFGA_SDCARD_DEVLINK:
+	case CFGA_SDCARD_DEV_CONFIGURE:
+	case CFGA_SDCARD_DEV_UNCONFIGURE:
+	case CFGA_SDCARD_NOT_CONNECTED:
+	case CFGA_SDCARD_DISCONNECTED:
+	case CFGA_SDCARD_NOT_CONFIGURED:
+	case CFGA_SDCARD_ALREADY_CONNECTED:
+	case CFGA_SDCARD_ALREADY_CONFIGURED:
+
+	case CFGA_SDCARD_RCM_HANDLE:
+	case CFGA_SDCARD_RCM_ONLINE:
+	case CFGA_SDCARD_RCM_OFFLINE:
+	case CFGA_SDCARD_RCM_REMOVE:
+		/* These messages also print ap_id.  */
+		set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "", NULL);
+		break;
+
+
+	case CFGA_SDCARD_IOCTL:
+		/* These messages also print errno.  */
+		{
+			char *errno_str = l_errno ? strerror(l_errno) : "";
+
+			set_msg(errstring, ERR_STR(rv), errno_str,
+			    l_errno ? "\n" : "", NULL);
+			break;
+		}
+
+	case CFGA_SDCARD_OPEN:
+		/* These messages also apid and errno.  */
+		{
+			char *errno_str = l_errno ? strerror(l_errno) : "";
+
+			set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "\n",
+			    errno_str, l_errno ? "\n" : "", NULL);
+			break;
+		}
+
+	default:
+		set_msg(errstring, ERR_STR(CFGA_SDCARD_INTERNAL_ERROR), NULL);
+
+	} /* end switch */
+
+
+	/*
+	 * Determine the proper error code to send back to the cfgadm library.
+	 */
+	return (sdcard_msgs[rv].cfga_err);
+}
+
+
+/*
+ * Entry points
+ */
+/* cfgadm entry point */
+/*ARGSUSED*/
+cfga_err_t
+cfga_change_state(
+	cfga_cmd_t state_change_cmd,
+	const char *ap_id,
+	const char *options,
+	struct cfga_confirm *confp,
+	struct cfga_msg *msgp,
+	char **errstring,
+	cfga_flags_t flags)
+{
+	int		ret;
+	ap_rstate_t	rstate;
+	ap_ostate_t	ostate;
+	devctl_hdl_t	hdl = NULL;
+	cfga_sdcard_ret_t	rv = CFGA_SDCARD_OK;
+	char		*pdyn;
+	int		i;
+	char		devpath[MAXPATHLEN];
+	char		msg[256];
+
+	/*
+	 * All sub-commands which can change state of device require
+	 * root privileges.
+	 */
+	if (geteuid() != 0) {
+		rv = CFGA_SDCARD_PRIV;
+		goto bailout;
+	}
+
+	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SDCARD_OK) {
+		(void) cfga_help(msgp, options, flags);
+		goto bailout;
+	}
+
+	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, DC_RDONLY)) !=
+	    CFGA_SDCARD_OK) {
+		goto bailout;
+	}
+
+	switch (state_change_cmd) {
+	case CFGA_CMD_CONFIGURE:
+		if ((rv = slot_state(hdl, &rstate, &ostate)) != CFGA_SDCARD_OK)
+			goto bailout;
+
+		if (ostate == AP_OSTATE_CONFIGURED) {
+			rv = CFGA_SDCARD_ALREADY_CONFIGURED;
+			goto bailout;
+		}
+		/* Disallow dynamic AP name component */
+		if (GET_DYN(ap_id) != NULL) {
+			rv = CFGA_SDCARD_INVALID_DEVNAME;
+			goto bailout;
+		}
+
+		if (rstate == AP_RSTATE_EMPTY) {
+			rv = CFGA_SDCARD_NOT_CONNECTED;
+			goto bailout;
+		}
+		rv = CFGA_SDCARD_OK;
+
+		if (devctl_ap_configure(hdl, NULL) != 0) {
+			rv = CFGA_SDCARD_DEV_CONFIGURE;
+			goto bailout;
+		}
+
+		for (i = 0; i < 15; i++) {
+			/*
+			 * We wait up to ~30 seconds for this to complete.
+			 * Hotplug is done asynchronously.
+			 */
+			rv = sdcard_get_devicepath(ap_id, devpath);
+			if (rv == CFGA_SDCARD_OK)
+				break;
+			(void) sleep(2);
+		}
+		if (rv != CFGA_SDCARD_OK) {
+			rv = CFGA_SDCARD_DEV_CONFIGURE;
+			goto bailout;
+		}
+
+		break;
+
+	case CFGA_CMD_UNCONFIGURE:
+		if ((rv = slot_state(hdl, &rstate, &ostate)) != CFGA_SDCARD_OK)
+			goto bailout;
+
+		if (rstate != AP_RSTATE_CONNECTED) {
+			rv = CFGA_SDCARD_NOT_CONNECTED;
+			goto bailout;
+		}
+
+		if (ostate != AP_OSTATE_CONFIGURED) {
+			rv = CFGA_SDCARD_NOT_CONFIGURED;
+			goto bailout;
+		}
+		/* Strip off AP name dynamic component, if present */
+		if ((pdyn = GET_DYN(ap_id)) != NULL) {
+			*pdyn = '\0';
+		}
+
+		rv = CFGA_SDCARD_OK;
+
+		/*LINTED E_SEC_PRINTF_VAR_FMT*/
+		(void) snprintf(msg, sizeof (msg),
+		    ERR_STR(CFGA_SDCARD_CONFIRM_UNCONFIGURE), ap_id);
+
+		if (!sdcard_confirm(confp, msg)) {
+			rv = CFGA_SDCARD_NACK;
+			break;
+		}
+
+		if (sdcard_get_devicepath(ap_id, devpath) != CFGA_SDCARD_OK) {
+			(void) printf("cfga_change_state: "
+			    "get device path failed\n");
+			rv = CFGA_SDCARD_DEV_UNCONFIGURE;
+			break;
+		}
+
+		rv = sdcard_rcm_offline(devpath, errstring, flags);
+		if (rv != CFGA_SDCARD_OK) {
+			break;
+		}
+
+		ret = devctl_ap_unconfigure(hdl, NULL);
+
+		if (ret != 0) {
+			rv = CFGA_SDCARD_DEV_UNCONFIGURE;
+			if (errno == EBUSY) {
+				rv = CFGA_SDCARD_BUSY;
+			}
+			sdcard_rcm_online(devpath, errstring);
+		} else {
+			sdcard_rcm_remove(devpath, errstring);
+		}
+
+		break;
+
+	case CFGA_CMD_DISCONNECT:
+		if ((rv = slot_state(hdl, &rstate, &ostate)) != CFGA_SDCARD_OK)
+			goto bailout;
+
+		if (rstate == AP_RSTATE_DISCONNECTED) {
+			rv = CFGA_SDCARD_DISCONNECTED;
+			goto bailout;
+		}
+
+		/* Strip off AP name dynamic component, if present */
+		if ((pdyn = GET_DYN(ap_id)) != NULL) {
+			*pdyn = '\0';
+		}
+
+
+		rv = CFGA_SDCARD_OK; /* other statuses don't matter */
+
+
+		/*
+		 * If the port originally with device attached and was
+		 * unconfigured already, the devicepath for the sd will be
+		 * removed. sdcard_get_devicepath in this case is not necessary.
+		 */
+
+		/* only call rcm_offline if the state was CONFIGURED */
+		if (ostate == AP_OSTATE_CONFIGURED) {
+			if (sdcard_get_devicepath(ap_id, devpath) !=
+			    CFGA_SDCARD_OK) {
+				(void) printf(
+				    "cfga_change_state: get path failed\n");
+				rv = CFGA_SDCARD_DEV_UNCONFIGURE;
+				break;
+			}
+
+			/*LINTED E_SEC_PRINTF_VAR_FMT*/
+			(void) snprintf(msg, sizeof (msg),
+			    ERR_STR(CFGA_SDCARD_CONFIRM_DISCONNECT), ap_id);
+			if (!sdcard_confirm(confp, msg)) {
+				rv = CFGA_SDCARD_NACK;
+				break;
+			}
+
+			rv = sdcard_rcm_offline(devpath, errstring, flags);
+			if (rv != CFGA_SDCARD_OK) {
+				break;
+			}
+
+			ret = devctl_ap_unconfigure(hdl, NULL);
+			if (ret != 0) {
+				(void) printf(
+				    "devctl_ap_unconfigure failed\n");
+				rv = CFGA_SDCARD_DEV_UNCONFIGURE;
+				if (errno == EBUSY)
+					rv = CFGA_SDCARD_BUSY;
+				sdcard_rcm_online(devpath, errstring);
+
+				/*
+				 * The current policy is that if unconfigure
+				 * failed, do not continue with disconnect.
+				 */
+				break;
+			} else {
+				(void) printf("%s\n",
+				    ERR_STR(CFGA_SDCARD_DEVICE_UNCONFIGURED));
+				sdcard_rcm_remove(devpath, errstring);
+			}
+		} else if (rstate == AP_RSTATE_CONNECTED ||
+		    rstate == AP_RSTATE_EMPTY) {
+			/*LINTED E_SEC_PRINTF_VAR_FMT*/
+			(void) snprintf(msg, sizeof (msg),
+			    ERR_STR(CFGA_SDCARD_CONFIRM_DISCONNECT), ap_id);
+
+			if (!sdcard_confirm(confp, msg)) {
+				rv = CFGA_SDCARD_NACK;
+				break;
+			}
+		}
+		ret = devctl_ap_disconnect(hdl, NULL);
+		if (ret != 0) {
+			rv = CFGA_SDCARD_IOCTL;
+			if (errno == EBUSY) {
+				rv = CFGA_SDCARD_BUSY;
+			}
+		}
+		break;
+
+	case CFGA_CMD_CONNECT:
+		if ((rv = slot_state(hdl, &rstate, &ostate)) != CFGA_SDCARD_OK)
+			goto bailout;
+
+		if (rstate == AP_RSTATE_CONNECTED) {
+			rv = CFGA_SDCARD_ALREADY_CONNECTED;
+			goto bailout;
+		}
+
+		/* Disallow dynamic AP name component */
+		if (GET_DYN(ap_id) != NULL) {
+			rv = CFGA_SDCARD_INVALID_DEVNAME;
+			goto bailout;
+		}
+
+		ret = devctl_ap_connect(hdl, NULL);
+		if (ret != 0) {
+			rv = CFGA_SDCARD_IOCTL;
+		} else {
+			rv = CFGA_SDCARD_OK;
+		}
+
+		break;
+
+	case CFGA_CMD_LOAD:
+	case CFGA_CMD_UNLOAD:
+		(void) cfga_help(msgp, options, flags);
+		rv = CFGA_SDCARD_OPNOTSUPP;
+		break;
+
+	case CFGA_CMD_NONE:
+	default:
+		(void) cfga_help(msgp, options, flags);
+		rv = CFGA_SDCARD_INTERNAL_ERROR;
+	}
+
+bailout:
+	cleanup_after_devctl_cmd(hdl);
+
+	return (sdcard_err_msg(errstring, rv, ap_id, errno));
+}
+
+/* cfgadm entry point */
+/*ARGSUSED*/
+cfga_err_t
+cfga_private_func(
+	const char *func,
+	const char *ap_id,
+	const char *options,
+	struct cfga_confirm *confp,
+	struct cfga_msg *msgp,
+	char **errstring,
+	cfga_flags_t flags)
+{
+	devctl_hdl_t		hdl = NULL;
+	cfga_sdcard_ret_t	rv;
+	char			*str_p;
+	char			msg[256];
+
+	if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_SDCARD_OK) {
+		(void) cfga_help(msgp, options, flags);
+		return (sdcard_err_msg(errstring, rv, ap_id, errno));
+	}
+
+	/*
+	 * All subcommands which can change state of device require
+	 * root privileges.
+	 */
+	if (geteuid() != 0) {
+		rv = CFGA_SDCARD_PRIV;
+		goto bailout;
+	}
+
+	if (func == NULL) {
+		rv = CFGA_SDCARD_OPTIONS;
+		goto bailout;
+	}
+
+	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, 0)) != CFGA_SDCARD_OK) {
+		goto bailout;
+	}
+
+	/* We do not care here about dynamic AP name component */
+	if ((str_p = GET_DYN(ap_id)) != NULL) {
+		*str_p = '\0';
+	}
+
+	if (strcmp(func, RESET_SLOT) == 0) {
+		/*LINTED E_SEC_PRINTF_VAR_FMT*/
+		(void) snprintf(msg, sizeof (msg),
+		    ERR_STR(CFGA_SDCARD_CONFIRM_RESET), ap_id);
+
+		if (!sdcard_confirm(confp, msg)) {
+			rv = CFGA_SDCARD_NACK;
+			goto bailout;
+		}
+		if ((rv = sdcard_reset_slot(ap_id)) != CFGA_SDCARD_OK) {
+			goto bailout;
+		}
+
+		rv = CFGA_SDCARD_OK;
+	} else {
+
+		/* Unrecognized operation request */
+		rv = CFGA_SDCARD_HWOPNOTSUPP;
+	}
+
+bailout:
+	cleanup_after_devctl_cmd(hdl);
+
+	return (sdcard_err_msg(errstring, rv, ap_id, errno));
+
+}
+
+/* cfgadm entry point */
+/*ARGSUSED*/
+cfga_err_t
+cfga_test(
+	const char *ap_id,
+	const char *options,
+	struct cfga_msg *msgp,
+	char **errstring,
+	cfga_flags_t flags)
+{
+	/* Should call ioctl for self test - phase 2 */
+	return (CFGA_OPNOTSUPP);
+}
+
+
+struct chk_dev {
+	int c_isblk;
+	char *c_minor;
+};
+
+/*ARGSUSED*/
+static int
+chk_dev_fcn(di_node_t node, di_minor_t minor, void *arg)
+{
+	char	*mn;
+	struct chk_dev *chkp = (struct chk_dev *)arg;
+
+	mn = di_minor_name(minor);
+	if (mn == NULL)
+		return (DI_WALK_CONTINUE);
+
+	if (strcmp(mn, chkp->c_minor) != 0)
+		return (DI_WALK_CONTINUE);
+
+	chkp->c_isblk = di_minor_spectype(minor) == S_IFBLK ? 1 : 0;
+
+	return (DI_WALK_TERMINATE);
+}
+
+/*
+ * Don't use devfs if stat() in /devices fails. Use libdevinfo instead.
+ * Retired devices don't show up in devfs.
+ *
+ *	Returns:
+ *		1 - minor exists and is of type BLK
+ *		0 - minor does not exist or is not of type BLK.
+ */
+static int
+is_devinfo_blk(char *minor_path)
+{
+	char	*minor_portion;
+	struct chk_dev chk_dev;
+	di_node_t node;
+	int	rv;
+
+	/*
+	 * prune minor path for di_init() - no /devices prefix and no minor name
+	 */
+	if (strncmp(minor_path, "/devices/", strlen("/devices/")) != 0)
+		return (0);
+
+	minor_portion = strrchr(minor_path, MINOR_SEP);
+	if (minor_portion == NULL)
+		return (0);
+
+	*minor_portion = 0;
+
+	node = di_init(minor_path + strlen("/devices"), DINFOMINOR);
+
+	*minor_portion = MINOR_SEP;
+
+	if (node == DI_NODE_NIL)
+		return (0);
+
+	chk_dev.c_isblk = 0;
+	chk_dev.c_minor = minor_portion + 1;
+
+	rv = di_walk_minor(node, NULL, 0, &chk_dev, chk_dev_fcn);
+
+	di_fini(node);
+
+	if (rv == 0 && chk_dev.c_isblk)
+		return (1);
+	else
+		return (0);
+}
+
+/*
+ * The dynamic component buffer returned by this function has to be freed!
+ */
+cfga_sdcard_ret_t
+sdcard_make_dyncomp(const char *ap_id, char **dyncomp)
+{
+	char	*cp = NULL;
+	int	l_errno;
+	char	devpath[MAXPATHLEN];
+	char	minor_path[MAXPATHLEN];
+	char	name_part[MAXNAMELEN];
+	char	*devlink = NULL;
+	char	*minor_portion = NULL;
+	int	deplen;
+	int	err;
+	DIR	*dp = NULL;
+	struct stat sb;
+	struct dirent *dep = NULL;
+	struct dirent *newdep = NULL;
+	char	*p;
+
+	assert(dyncomp != NULL);
+
+	/*
+	 * Get target node path
+	 */
+	if (sdcard_get_devicepath(ap_id, devpath) != CFGA_SDCARD_OK) {
+
+		(void) printf("cfga_list_ext: cannot locate target device\n");
+		return (CFGA_SDCARD_DYNAMIC_AP);
+
+	} else {
+
+		cp = strrchr(devpath, PATH_SEP);
+		assert(cp != NULL);
+
+		/*
+		 * If the child node is the sdcard node, then what we really
+		 * want is the grandchild.  But we know that the grandchild
+		 * will always be disk@0,0.
+		 */
+		if (strstr(cp, "/sdcard@") == cp) {
+			/* sdcard nodes have disk children, if any */
+			(void) strlcat(devpath, "/disk@0,0", sizeof (devpath));
+			cp = strrchr(cp, PATH_SEP);
+		}
+		*cp = 0;	/* terminate path for opendir() */
+
+		(void) strncpy(name_part, cp + 1, MAXNAMELEN);
+
+		/*
+		 * Using libdevinfo for this is overkill and kills
+		 * performance when many consumers are using libcfgadm
+		 * concurrently.
+		 */
+		if ((dp = opendir(devpath)) == NULL) {
+			goto bailout;
+		}
+
+		/*
+		 * deplen is large enough to fit the largest path-
+		 * struct dirent includes one byte (the terminator)
+		 * so we don't add 1 to the calculation here.
+		 */
+		deplen = pathconf(devpath, _PC_NAME_MAX);
+		deplen = ((deplen <= 0) ? MAXNAMELEN : deplen) +
+		    sizeof (struct dirent);
+		dep = (struct dirent *)malloc(deplen);
+		if (dep == NULL)
+			goto bailout;
+
+		while ((err = readdir_r(dp, dep, &newdep)) == 0 &&
+		    newdep != NULL) {
+
+			assert(newdep == dep);
+
+			if (strcmp(dep->d_name, ".") == 0 ||
+			    strcmp(dep->d_name, "..") == 0 ||
+			    (minor_portion = strchr(dep->d_name,
+			    MINOR_SEP)) == NULL)
+				continue;
+
+			*minor_portion = 0;
+			if (strcmp(dep->d_name, name_part) != 0)
+				continue;
+			*minor_portion = MINOR_SEP;
+
+			(void) snprintf(minor_path, MAXPATHLEN,
+			    "%s/%s", devpath, dep->d_name);
+
+			/*
+			 * If stat() fails, the device *may* be retired.
+			 * Check via libdevinfo if the device has a BLK minor.
+			 * We don't use libdevinfo all the time, since taking
+			 * a snapshot is slower than a stat().
+			 */
+			if (stat(minor_path, &sb) < 0) {
+				if (is_devinfo_blk(minor_path)) {
+					break;
+				} else {
+					continue;
+				}
+			}
+
+			if (S_ISBLK(sb.st_mode))
+				break;
+		}
+
+		(void) closedir(dp);
+		free(dep);
+
+		dp = NULL;
+		dep = NULL;
+
+		/*
+		 * If there was an error, or we didn't exit the loop
+		 * by finding a block or character device, bail out.
+		 */
+		if (err != 0 || newdep == NULL)
+			goto bailout;
+
+		/*
+		 * Look for links to the physical path in /dev/dsk,
+		 * since we ONLY looked for BLOCK devices above.
+		 */
+
+		(void) physpath_to_devlink("/dev/dsk",
+		    minor_path, &devlink, &l_errno);
+
+		/* postprocess and copy logical name here */
+		if (devlink != NULL) {
+			/*
+			 * For disks, remove partition/slice info
+			 */
+			if ((cp = strstr(devlink, "dsk/")) != NULL) {
+				/* cXtYdZ[(s[0..15])|(p[0..X])] */
+				if ((p = strchr(cp + 4, 'd')) != NULL) {
+					p++;	/* Skip the 'd' */
+					while (*p != 0 && isdigit(*p))
+						p++;
+					*p = 0;
+				}
+				*dyncomp = strdup(cp);
+			}
+
+			free(devlink);
+		}
+
+		return (CFGA_SDCARD_OK);
+	}
+
+bailout:
+	if (dp)
+		(void) closedir(dp);
+	if (dep)
+		free(dep);
+	return (CFGA_SDCARD_DYNAMIC_AP);
+}
+
+void
+sdcard_clean_string(char *s, int sz)
+{
+	int	len;
+	char	*p;
+
+	/* ensure null termination */
+	s[sz - 1] = '\0';
+	p = s;
+
+	/* strip leading white space */
+	while (*p == ' ') p++;
+	(void) memmove(s, p, strlen(p));
+
+	len = strlen(s) - 1;
+	/* trim trailing space */
+	while ((len >= 0) && (s[len] == ' ')) {
+		s[len] = '\0';
+		len--;
+	}
+
+	for (/* nop */; len >= 0; len--) {
+		char	c = s[len];
+		if (((c >= 'a') && (c <= 'z')) ||
+		    ((c >= 'A') && (c <= 'Z')) ||
+		    ((c >= '0') && (c <= '9')) ||
+		    (c == '_') || (c == '+') || (c == '-'))
+			continue;
+		s[len] = '_';
+	}
+}
+
+/* cfgadm entry point */
+/*ARGSUSED*/
+cfga_err_t
+cfga_list_ext(
+	const char *ap_id,
+	cfga_list_data_t **ap_id_list,
+	int *nlistp,
+	const char *options,
+	const char *listopts,
+	char **errstring,
+	cfga_flags_t flags)
+{
+	int			l_errno;
+	char			*ap_id_log = NULL;
+	devctl_hdl_t		devctl_hdl = NULL;
+	cfga_sdcard_ret_t	rv = CFGA_SDCARD_OK;
+	devctl_ap_state_t	devctl_ap_state;
+	char			*pdyn;
+
+
+	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_SDCARD_OK) {
+		(void) cfga_help(NULL, options, flags);
+		goto bailout;
+	}
+	/* We do not care here about dynamic AP name component */
+	if ((pdyn = GET_DYN(ap_id)) != NULL) {
+		*pdyn = '\0';
+	}
+
+	if (ap_id_list == NULL || nlistp == NULL) {
+		rv = CFGA_SDCARD_DATA_ERROR;
+		(void) cfga_help(NULL, options, flags);
+		goto bailout;
+	}
+
+	/* Get ap status */
+	if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, DC_RDONLY)) !=
+	    CFGA_SDCARD_OK) {
+		goto bailout;
+	}
+
+	/* will call dc_cmd to send IOCTL to kernel */
+	if (devctl_ap_getstate(devctl_hdl, NULL, &devctl_ap_state) == -1) {
+		cleanup_after_devctl_cmd(devctl_hdl);
+		rv = CFGA_SDCARD_IOCTL;
+		goto bailout;
+	}
+
+	cleanup_after_devctl_cmd(devctl_hdl);
+
+	/*
+	 * Create cfga_list_data_t struct.
+	 */
+	if ((*ap_id_list =
+	    (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) {
+		rv = CFGA_SDCARD_ALLOC_FAIL;
+		goto bailout;
+	}
+	*nlistp = 1;
+
+	/*
+	 * Rest of the code fills in the cfga_list_data_t struct.
+	 */
+
+	/* Get /dev/cfg path to corresponding to the physical ap_id */
+	/* Remember ap_id_log must be freed */
+	rv = physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id,
+	    &ap_id_log, &l_errno);
+
+	if (rv != 0) {
+		rv = CFGA_SDCARD_DEVLINK;
+		goto bailout;
+	}
+
+	/* Get logical ap_id corresponding to the physical */
+	if (ap_id_log == NULL || strstr(ap_id_log, CFGA_DEV_DIR) == NULL) {
+		rv = CFGA_SDCARD_DEVLINK;
+		goto bailout;
+	}
+
+	(void) strlcpy((*ap_id_list)->ap_log_id,
+	    /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1,
+	    sizeof ((*ap_id_list)->ap_log_id));
+
+	free(ap_id_log);
+	ap_id_log = NULL;
+
+	(void) strlcpy((*ap_id_list)->ap_phys_id, ap_id,
+	    sizeof ((*ap_id_list)->ap_phys_id));
+
+	switch (devctl_ap_state.ap_rstate) {
+		case AP_RSTATE_EMPTY:
+			(*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY;
+			break;
+
+		case AP_RSTATE_DISCONNECTED:
+			(*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED;
+			break;
+
+		case AP_RSTATE_CONNECTED:
+			(*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED;
+			break;
+
+		default:
+			rv = CFGA_SDCARD_STATE;
+			goto bailout;
+	}
+
+	switch (devctl_ap_state.ap_ostate) {
+		case AP_OSTATE_CONFIGURED:
+			(*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED;
+			break;
+
+		case AP_OSTATE_UNCONFIGURED:
+			(*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED;
+			break;
+
+		default:
+			rv = CFGA_SDCARD_STATE;
+			goto bailout;
+	}
+
+	switch (devctl_ap_state.ap_condition) {
+		case AP_COND_OK:
+			(*ap_id_list)->ap_cond = CFGA_COND_OK;
+			break;
+
+		case AP_COND_FAILING:
+			(*ap_id_list)->ap_cond = CFGA_COND_FAILING;
+			break;
+
+		case AP_COND_FAILED:
+			(*ap_id_list)->ap_cond = CFGA_COND_FAILED;
+			break;
+
+		case AP_COND_UNUSABLE:
+			(*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE;
+			break;
+
+		case AP_COND_UNKNOWN:
+			(*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN;
+			break;
+
+		default:
+			rv = CFGA_SDCARD_STATE;
+			goto bailout;
+	}
+
+	(*ap_id_list)->ap_class[0] = '\0';	/* Filled by libcfgadm */
+	(*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition;
+	(*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change;
+	(*ap_id_list)->ap_info[0] = NULL;
+
+	if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) {
+		sda_card_info_t		ci;
+		char			*ct;
+
+		/*
+		 * Fill in the 'Information' field for the -v option
+		 */
+		rv = do_control_ioctl(ap_id, SDA_CFGA_GET_CARD_INFO,
+		    &ci, sizeof (ci));
+		if (rv != CFGA_SDCARD_OK) {
+			goto bailout;
+		}
+
+		switch (ci.ci_type) {
+		case SDA_CT_MMC:
+		case SDA_CT_SDMEM:
+		case SDA_CT_SDHC:
+		case SDA_CT_SDCOMBO:
+			/* these are all memory cards */
+			sdcard_clean_string(ci.ci_pid, sizeof (ci.ci_pid));
+
+			/*
+			 * We don't display the mfg id, because we
+			 * have no reliable way to look it up.
+			 */
+			(void) snprintf((*ap_id_list)->ap_info,
+			    sizeof ((*ap_id_list)->ap_info),
+			    "Mod: %s Rev: %d.%d Date: %d/%d SN: %X",
+			    ci.ci_pid[0] ? ci.ci_pid : "?",
+			    ci.ci_major, ci.ci_minor,
+			    ci.ci_month, (int)ci.ci_year + 1900,
+			    ci.ci_serial);
+			break;
+		default:
+			/*
+			 * we don't know what this is really... need to
+			 * parse CIS later.
+			 */
+			(void) strlcpy((*ap_id_list)->ap_info, "",
+			    sizeof ((*ap_id_list)->ap_info));
+			break;
+		}
+
+		switch (ci.ci_type) {
+		case SDA_CT_UNKNOWN:
+			ct = "unknown";
+			break;
+		case SDA_CT_MMC:
+			ct = "mmc";
+			break;
+		case SDA_CT_SDMEM:
+			ct = "sdcard";
+			break;
+		case SDA_CT_SDHC:
+			ct = "sdhc";
+			break;
+		case SDA_CT_SDCOMBO:
+			ct = "sd-combo";
+			break;
+		case SDA_CT_SDIO:
+			ct = "sdio";
+			break;
+		}
+
+		(void) strlcpy((*ap_id_list)->ap_type, ct,
+		    sizeof ((*ap_id_list)->ap_type));
+
+		if ((*ap_id_list)->ap_o_state == CFGA_STAT_CONFIGURED) {
+
+			char *dyncomp = NULL;
+
+			/*
+			 * This is the case where we need to generate
+			 * a dynamic component of the ap_id, i.e. device.
+			 */
+			(void) sdcard_make_dyncomp(ap_id, &dyncomp);
+			if (dyncomp != NULL) {
+				(void) strcat((*ap_id_list)->ap_log_id,
+				    DYN_SEP);
+				(void) strlcat((*ap_id_list)->ap_log_id,
+				    dyncomp,
+				    sizeof ((*ap_id_list)->ap_log_id));
+				free(dyncomp);
+			}
+		}
+
+	} else {
+		(void) strlcpy((*ap_id_list)->ap_type, "sdcard-slot",
+		    sizeof ((*ap_id_list)->ap_type));
+	}
+
+	return (sdcard_err_msg(errstring, rv, ap_id, errno));
+
+bailout:
+	if (*ap_id_list != NULL) {
+		free(*ap_id_list);
+	}
+	if (ap_id_log != NULL) {
+		free(ap_id_log);
+	}
+
+	return (sdcard_err_msg(errstring, rv, ap_id, errno));
+}
+
+/*
+ * This routine accepts a string and prints it using
+ * the message print routine argument.
+ */
+static void
+cfga_msg(struct cfga_msg *msgp, const char *str)
+{
+	int len;
+	char *q;
+
+	if (msgp == NULL || msgp->message_routine == NULL) {
+		(void) printf("cfga_msg: NULL msgp\n");
+		return;
+	}
+
+	if ((len = strlen(str)) == 0) {
+		(void) printf("cfga_msg: null str\n");
+		return;
+	}
+
+	if ((q = (char *)calloc(len + 1, 1)) == NULL) {
+		perror("cfga_msg");
+		return;
+	}
+
+	(void) strcpy(q, str);
+	(*msgp->message_routine)(msgp->appdata_ptr, q);
+
+	free(q);
+}
+
+/* cfgadm entry point */
+/*ARGSUSED*/
+cfga_err_t
+cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
+{
+	if (options != NULL) {
+		cfga_msg(msgp,
+		    dgettext(TEXT_DOMAIN, sdcard_help[HELP_UNKNOWN]));
+		cfga_msg(msgp, options);
+	}
+	cfga_msg(msgp, dgettext(TEXT_DOMAIN, sdcard_help[HELP_HEADER]));
+	cfga_msg(msgp, sdcard_help[HELP_CONFIG]);
+	cfga_msg(msgp, sdcard_help[HELP_RESET_SLOT]);
+
+	return (CFGA_OK);
+}
+
+
+/*
+ * Ensure the ap_id passed is in the correct (physical ap_id) form:
+ *     path/device:xx
+ * where xx is a one or two-digit number.
+ *
+ * Note the library always calls the plugin with a physical ap_id.
+ */
+static int
+verify_valid_apid(const char *ap_id)
+{
+	char	*l_ap_id;
+
+	if (ap_id == NULL)
+		return (-1);
+
+	l_ap_id = strrchr(ap_id, MINOR_SEP);
+	l_ap_id++;
+
+	if (strspn(l_ap_id, "0123456789") != strlen(l_ap_id)) {
+		/* Bad characters in the ap_id */
+		return (-1);
+	}
+
+	return (0);
+}
+
+
+
+/*
+ * Verify the params passed in are valid.
+ */
+static cfga_sdcard_ret_t
+verify_params(const char *ap_id, const char *options, char **errstring)
+{
+	char *pdyn, *lap_id;
+	int rv;
+
+	if (errstring != NULL) {
+		*errstring = NULL;
+	}
+
+	if (options != NULL) {
+		return (CFGA_SDCARD_OPTIONS);
+	}
+
+	/* Strip dynamic AP name component if it is present. */
+	lap_id = strdup(ap_id);
+	if (lap_id == NULL) {
+		return (CFGA_SDCARD_ALLOC_FAIL);
+	}
+	if ((pdyn = GET_DYN(lap_id)) != NULL) {
+		*pdyn = '\0';
+	}
+
+	if (verify_valid_apid(lap_id) != 0) {
+		rv = CFGA_SDCARD_AP;
+	} else {
+		rv = CFGA_SDCARD_OK;
+	}
+	free(lap_id);
+
+	return (rv);
+}
+
+/*
+ * Pair of routines to set up for/clean up after a devctl_ap_* lib call.
+ */
+static void
+cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl)
+{
+	if (devctl_hdl != NULL) {
+		devctl_release(devctl_hdl);
+	}
+}
+
+static cfga_sdcard_ret_t
+setup_for_devctl_cmd(const char *ap_id, devctl_hdl_t *devctl_hdl, uint_t oflag)
+{
+	char *lap_id, *pdyn;
+
+	lap_id = strdup(ap_id);
+	if (lap_id == NULL)
+		return (CFGA_SDCARD_ALLOC_FAIL);
+	if ((pdyn = GET_DYN(lap_id)) != NULL) {
+		*pdyn = '\0';
+	}
+
+	/* Get a devctl handle to pass to the devctl_ap_XXX functions */
+	if ((*devctl_hdl = devctl_ap_acquire(lap_id, oflag)) == NULL) {
+		(void) fprintf(stderr, "[libcfgadm:sdcard] "
+		    "setup_for_devctl_cmd: devctl_ap_acquire failed: %s\n",
+		    strerror(errno));
+		free(lap_id);
+		return (CFGA_SDCARD_DEVCTL);
+	}
+
+	free(lap_id);
+	return (CFGA_SDCARD_OK);
+}
+
+
+static cfga_sdcard_ret_t
+slot_state(devctl_hdl_t hdl, ap_rstate_t *rstate, ap_ostate_t *ostate)
+{
+	devctl_ap_state_t	devctl_ap_state;
+
+	if (devctl_ap_getstate(hdl, NULL, &devctl_ap_state) == -1) {
+		(void) printf("devctl_ap_getstate failed, errno: %d\n", errno);
+		return (CFGA_SDCARD_IOCTL);
+	}
+	*rstate = devctl_ap_state.ap_rstate;
+	*ostate =  devctl_ap_state.ap_ostate;
+	return (CFGA_SDCARD_OK);
+}
+
+/*
+ * Given a subcommand to the DEVCTL_AP_CONTROL ioctl, rquest the size of
+ * the data to be returned, allocate a buffer, then get the data.
+ */
+cfga_sdcard_ret_t
+do_control_ioctl(const char *ap_id, int subcommand, void *data, size_t size)
+{
+	int			fd = -1;
+	cfga_sdcard_ret_t	rv = CFGA_SDCARD_OK;
+	struct sda_ap_control	apc;
+
+	if ((fd = open(ap_id, O_RDONLY)) == -1) {
+		(void) printf("do_control_ioctl: open: errno:%d\n", errno);
+		rv = CFGA_SDCARD_OPEN;
+		goto bailout;
+	}
+
+	apc.cmd = subcommand;
+	apc.data = data;
+	apc.size = size;
+
+	/* Execute IOCTL */
+	if (ioctl(fd, DEVCTL_AP_CONTROL, &apc) != 0) {
+		rv = CFGA_SDCARD_IOCTL;
+		goto bailout;
+	}
+
+	(void) close(fd);
+
+	return (rv);
+
+bailout:
+	if (fd != -1) {
+		(void) close(fd);
+	}
+
+	if ((rv != CFGA_SDCARD_OK) && (errno == EBUSY)) {
+		rv = CFGA_SDCARD_BUSY;
+	}
+
+	return (rv);
+}
+
+
+static int
+sdcard_confirm(struct cfga_confirm *confp, char *msg)
+{
+	int rval;
+
+	if (confp == NULL || confp->confirm == NULL) {
+		return (0);
+	}
+	rval = (*confp->confirm)(confp->appdata_ptr, msg);
+
+	return (rval);
+}
+
+
+cfga_sdcard_ret_t
+sdcard_get_devicepath(const char *ap_id, char *devpath)
+{
+	return (do_control_ioctl(ap_id, SDA_CFGA_GET_DEVICE_PATH,
+	    devpath, MAXPATHLEN));
+}
+
+cfga_sdcard_ret_t
+sdcard_reset_slot(const char *ap_id)
+{
+	return (do_control_ioctl(ap_id, SDA_CFGA_RESET_SLOT, NULL, 0));
+}
+
+static rcm_handle_t *rcm_handle = NULL;
+static mutex_t rcm_handle_lock = DEFAULTMUTEX;
+
+/*
+ * sdcard_rcm_offline:
+ *      Offline resource consumers.
+ */
+cfga_sdcard_ret_t
+sdcard_rcm_offline(char *devpath, char **errstring, cfga_flags_t flags)
+{
+	int			rret;
+	uint_t			rflags;
+	rcm_info_t		*rinfo = NULL;
+	cfga_sdcard_ret_t	ret;
+
+	if ((ret = sdcard_rcm_init()) != CFGA_SDCARD_OK) {
+		return (ret);
+	}
+
+	/* Translate the cfgadm flags to RCM flags */
+	rflags = (flags & CFGA_FLAG_FORCE) ? RCM_FORCE : 0;
+
+	rret = rcm_request_offline(rcm_handle, devpath, rflags, &rinfo);
+	if (rret != RCM_SUCCESS) {
+		if (rinfo) {
+			sdcard_rcm_info_table(rinfo, errstring);
+			rcm_free_info(rinfo);
+			rinfo = NULL;
+		}
+
+		if (rret == RCM_FAILURE) {
+			sdcard_rcm_online(devpath, errstring);
+		}
+		ret = CFGA_SDCARD_RCM_OFFLINE;
+	}
+	return (ret);
+}
+
+
+/*
+ * sdcard_rcm_online:
+ *      Online resource consumers that were previously offlined.
+ */
+void
+sdcard_rcm_online(char *devpath, char **errstring)
+{
+	rcm_info_t		*rinfo = NULL;
+
+	if (sdcard_rcm_init() != CFGA_SDCARD_OK) {
+		return;
+	}
+
+	if (rcm_notify_online(rcm_handle, devpath, 0, &rinfo) !=
+	    RCM_SUCCESS && (rinfo != NULL)) {
+		sdcard_rcm_info_table(rinfo, errstring);
+		rcm_free_info(rinfo);
+		rinfo = NULL;
+	}
+}
+
+/*
+ * sdcard_rcm_remove:
+ *      Remove resource consumers after their kernel removal.
+ */
+void
+sdcard_rcm_remove(char *devpath, char **errstring)
+{
+	rcm_info_t		*rinfo = NULL;
+
+	if (sdcard_rcm_init() != CFGA_SDCARD_OK) {
+		return;
+	}
+
+	if (rcm_notify_remove(rcm_handle, devpath, 0, &rinfo) !=
+	    RCM_SUCCESS && (rinfo != NULL)) {
+
+		sdcard_rcm_info_table(rinfo, errstring);
+		rcm_free_info(rinfo);
+		rinfo = NULL;
+	}
+}
+
+
+/*
+ * sdcard_rcm_init:
+ * Contains common initialization code for entering a sdcard_rcm_xx() routine.
+ */
+static cfga_sdcard_ret_t
+sdcard_rcm_init(void)
+{
+	/* Get a handle for the RCM operations */
+	(void) mutex_lock(&rcm_handle_lock);
+	if (rcm_handle == NULL) {
+		if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &rcm_handle) !=
+		    RCM_SUCCESS) {
+			(void) mutex_unlock(&rcm_handle_lock);
+
+			return (CFGA_SDCARD_RCM_HANDLE);
+		}
+	}
+	(void) mutex_unlock(&rcm_handle_lock);
+
+	return (CFGA_SDCARD_OK);
+}
+
+
+#define	MAX_FORMAT	80	/* for info table */
+
+/*
+ * sdcard_rcm_info_table:
+ * Takes an opaque rcm_info_t pointer and a character pointer,
+ * and appends the rcm_info_t data in the form of a table to the
+ * given character pointer.
+ */
+static void
+sdcard_rcm_info_table(rcm_info_t *rinfo, char **table)
+{
+	int i;
+	size_t w;
+	size_t width = 0;
+	size_t w_rsrc = 0;
+	size_t w_info = 0;
+	size_t table_size = 0;
+	uint_t tuples = 0;
+	rcm_info_tuple_t *tuple = NULL;
+	char *rsrc;
+	char *info;
+	char *newtable;
+	static char format[MAX_FORMAT];
+	const char *infostr;
+
+	/* Protect against invalid arguments */
+	if (rinfo == NULL || table == NULL) {
+		return;
+	}
+
+	/* Set localized table header strings */
+	rsrc = dgettext(TEXT_DOMAIN, "Resource");
+	info = dgettext(TEXT_DOMAIN, "Information");
+
+
+	/* A first pass, to size up the RCM information */
+	while (tuple = rcm_info_next(rinfo, tuple)) {
+		if ((infostr = rcm_info_info(tuple)) != NULL) {
+			tuples++;
+			if ((w = strlen(rcm_info_rsrc(tuple))) > w_rsrc)
+				w_rsrc = w;
+			if ((w = strlen(infostr)) > w_info)
+				w_info = w;
+		}
+	}
+
+	/* If nothing was sized up above, stop early */
+	if (tuples == 0) {
+		return;
+	}
+
+	/* Adjust column widths for column headings */
+	if ((w = strlen(rsrc)) > w_rsrc) {
+		w_rsrc = w;
+	} else if ((w_rsrc - w) % 2) {
+		w_rsrc++;
+	}
+
+	if ((w = strlen(info)) > w_info) {
+		w_info = w;
+	} else if ((w_info - w) % 2) {
+		w_info++;
+	}
+
+
+	/*
+	 * Compute the total line width of each line,
+	 * accounting for intercolumn spacing.
+	 */
+	width = w_info + w_rsrc + 4;
+
+	/* Allocate space for the table */
+	table_size = (2 + tuples) * (width + 1) + 2;
+	if (*table == NULL) {
+		/* zero fill for the strcat() call below */
+		*table = calloc(table_size, sizeof (char));
+		if (*table == NULL) {
+			return;
+		}
+	} else {
+		newtable = realloc(*table, strlen(*table) + table_size);
+		if (newtable == NULL) {
+			return;
+		} else {
+			*table = newtable;
+		}
+	}
+
+	/* Place a table header into the string */
+
+
+	/* The resource header */
+	(void) strcat(*table, "\n");
+	w = strlen(rsrc);
+
+	for (i = 0; i < ((w_rsrc - w) / 2); i++) {
+		(void) strcat(*table, " ");
+	}
+	(void) strcat(*table, rsrc);
+
+	for (i = 0; i < ((w_rsrc - w) / 2); i++) {
+		(void) strcat(*table, " ");
+	}
+
+	/* The information header */
+	(void) strcat(*table, "  ");
+	w = strlen(info);
+	for (i = 0; i < ((w_info - w) / 2); i++) {
+		(void) strcat(*table, " ");
+	}
+	(void) strcat(*table, info);
+
+	for (i = 0; i < ((w_info - w) / 2); i++) {
+		(void) strcat(*table, " ");
+	}
+
+	(void) strcat(*table, "\n");
+
+	/* Underline the headers */
+	for (i = 0; i < w_rsrc; i++) {
+		(void) strcat(*table, "-");
+	}
+
+	(void) strcat(*table, "  ");
+	for (i = 0; i < w_info; i++) {
+		(void) strcat(*table, "-");
+	}
+
+
+	(void) strcat(*table, "\n");
+
+	/* Construct the format string */
+	(void) snprintf(format, MAX_FORMAT, "%%-%ds  %%-%ds",
+	    (int)w_rsrc, (int)w_info);
+
+	/* Add the tuples to the table string */
+	tuple = NULL;
+	while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) {
+		if ((infostr = rcm_info_info(tuple)) != NULL) {
+			(void) sprintf(&((*table)[strlen(*table)]),
+			    format, rcm_info_rsrc(tuple), infostr);
+			(void) strcat(*table, "\n");
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.h	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,138 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CFGA_SDCARD_H
+#define	_CFGA_SDCARD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <libdevice.h>
+#include <sys/varargs.h>
+
+#include <libdevinfo.h>
+#include <libdevice.h>
+#include <librcm.h>
+#include <synch.h>
+#include <thread.h>
+#include <assert.h>
+#include <sys/sdcard/sda_ioctl.h>
+
+#define	CFGA_PLUGIN_LIB
+#include <config_admin.h>
+
+/* Misc text strings */
+#define	CFGA_DEV_DIR			"/dev/cfg"
+#define	DYN_SEP				"::"
+#define	CFGA_DEVCTL_NODE  		":devctl"
+#define	MINOR_SEP 			':'
+#define	PATH_SEP 			'/'
+
+#define	RESET_SLOT		"sdcard_reset_slot"
+
+/* for confirm operation */
+#define	SDCARD_CONFIRM_1 \
+	"This operation will suspend activity on the SD card device\nContinue"
+#define	SDCARD_CONFIRM_2 \
+	"This operation will disrupt activity on the SD card device\nContinue"
+
+#define	GET_DYN(a)		(((a) != NULL) ? \
+				strstr((a), DYN_SEP) : (void *)0)
+
+
+/* Messages */
+
+typedef struct msgcvt {
+	int		intl;		/* Flag: if 1, internationalize */
+	cfga_err_t	cfga_err;	/* Error code libcfgadm understands */
+	const char	*msgstr;
+} msgcvt_t;
+
+#define	NO_CVT	0
+#define	CVT	1
+
+#define	MSG_TBL_SZ(table)	(sizeof ((table)) / sizeof (msgcvt_t))
+
+/* Messages */
+
+
+/* Error message ids (and indices into sdcard_error_msgs) */
+typedef enum {
+	CFGA_SDCARD_OK = 0,
+	CFGA_SDCARD_NACK,
+	CFGA_SDCARD_UNKNOWN,
+	CFGA_SDCARD_PRIV,
+	CFGA_SDCARD_DYNAMIC_AP,
+	CFGA_SDCARD_INTERNAL_ERROR,
+	CFGA_SDCARD_ALLOC_FAIL,
+	CFGA_SDCARD_IOCTL,
+	CFGA_SDCARD_DEVCTL,
+	CFGA_SDCARD_AP,
+	CFGA_SDCARD_BUSY,
+	CFGA_SDCARD_DEVLINK,
+	CFGA_SDCARD_INVALID_DEVNAME,
+	CFGA_SDCARD_DATA_ERROR,
+	CFGA_SDCARD_DEV_CONFIGURE,
+	CFGA_SDCARD_DEV_UNCONFIGURE,
+	CFGA_SDCARD_NOT_CONNECTED,
+	CFGA_SDCARD_DISCONNECTED,
+	CFGA_SDCARD_NOT_CONFIGURED,
+	CFGA_SDCARD_ALREADY_CONNECTED,
+	CFGA_SDCARD_ALREADY_CONFIGURED,
+	CFGA_SDCARD_DEVICE_UNCONFIGURED,
+	CFGA_SDCARD_OPNOTSUPP,
+	CFGA_SDCARD_HWOPNOTSUPP,
+	CFGA_SDCARD_OPTIONS,
+	CFGA_SDCARD_STATE,
+	CFGA_SDCARD_OPEN,
+	CFGA_SDCARD_RCM_HANDLE,
+	CFGA_SDCARD_RCM_OFFLINE,
+	CFGA_SDCARD_RCM_REMOVE,
+	CFGA_SDCARD_RCM_ONLINE,
+	CFGA_SDCARD_CONFIRM_RESET,
+	CFGA_SDCARD_CONFIRM_UNCONFIGURE,
+	CFGA_SDCARD_CONFIRM_DISCONNECT
+} cfga_sdcard_ret_t;
+
+/*
+ * Given an error msg index, look up the associated string, and
+ * convert it to the current locale if required.
+ */
+#define	ERR_STR(msg_idx) \
+	(get_msg((msg_idx), sdcard_msgs, MSG_TBL_SZ(sdcard_msgs)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _CFGA_SDCARD_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/common/mapfile-vers	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+SUNWprivate_1.1 {
+    global:
+	cfga_change_state;
+	cfga_help;
+	cfga_list_ext;
+	cfga_private_func;
+	cfga_test;
+	cfga_version;
+    local:
+	*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/i386/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+CFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+LINTFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS) 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/sdcard.xcl	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,70 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# lib/cfgadm_plugins/sdcard/sdcard.xcl
+#
+msgid "/devices/"
+msgid "/devices"
+msgid "/"
+msgid "."
+msgid "C"
+msgid "\n"
+msgid "-"
+msgid "ap_id: "
+msgid " %s%s\n%s"
+msgid "%s/%s"
+msgid "%s\n"
+msgid "../"
+msgid ""
+msgid "cfga_change_state: get device path failed\n"
+msgid "cfga_change_state: get path failed\n"
+msgid "devctl_ap_unconfigure failed\n"
+#msgid "No valid option specified\n"
+msgid "cfga_list_ext: cannot locate target device\n"
+msgid "dsk/"
+msgid "/dev/dsk"
+msgid "/dsk/"
+msgid "/rdsk/"
+msgid "/dev/"
+msgid "/disk@0,0"
+msgid "/sdcard@"
+msgid "Mod: %s Rev: %d.%d Date: %d/%d SN: %X"
+msgid "::"
+msgid "cfga_msg"
+msgid "cfga_msg: NULL msgp\n"
+msgid "cfga_msg: null str\n"
+msgid "0123456789"
+msgid ".."
+msgid "[libcfgadm:sdcard] "
+      "setup_for_devctl_cmd: devctl_ap_acquire failed: %s\n"
+msgid "devctl_ap_getstate failed, errno: %d\n"
+msgid "do_control_ioctl: open: errno:%d\n"
+msgid "ioctl failed (size)"
+msgid " "
+msgid "  "
+msgid "%%-%ds  %%-%ds"
+msgid " cfgadm -c [configure|unconfigure|disconnect|connect] ap_id "
+      "[ap_id...]\n"
+msgid " cfgadm -x sdcard_reset_slot ap_id [ap_id...]\n"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/sparc/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+CFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+LINTFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS) 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/cfgadm_plugins/sdcard/sparcv9/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+CFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+LINTFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) 
--- a/usr/src/pkgdefs/Makefile	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 include $(SRC)/Makefile.master
 
@@ -99,7 +97,8 @@
 	SUNWstc.u \
 	SUNWus.u \
 	SUNWust1.v \
-	SUNWust2.v
+	SUNWust2.v \
+	SUNWwbsd
 
 sparc_XMODS=
 
@@ -370,6 +369,7 @@
 	SUNWscplp  \
 	SUNWscsa1394  \
 	SUNWscsip \
+	SUNWsdcard \
 	SUNWses \
 	SUNWsfe \
 	SUNWslpr \
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -187,6 +185,7 @@
 f none kernel/misc/acpica 755 root sys
 f none kernel/misc/agpmaster 755 root sys
 f none kernel/misc/bignum 755 root sys
+f none kernel/misc/blk2scsa 755 root sys
 f none kernel/misc/bootdev 755 root sys
 f none kernel/misc/busra 755 root sys
 f none kernel/misc/cardbus 755 root sys
@@ -394,6 +393,7 @@
 f none kernel/misc/amd64/acpica 755 root sys
 f none kernel/misc/amd64/agpmaster 755 root sys
 f none kernel/misc/amd64/bignum 755 root sys
+f none kernel/misc/amd64/blk2scsa 755 root sys
 f none kernel/misc/amd64/bootdev 755 root sys
 f none kernel/misc/amd64/busra 755 root sys
 f none kernel/misc/amd64/cardbus 755 root sys
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -182,6 +180,7 @@
 f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_tape 755 root sys
 f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_tpgs_tape 755 root sys
 d none kernel/misc/sparcv9 755 root sys
+f none kernel/misc/sparcv9/blk2scsa 755 root sys
 f none kernel/misc/sparcv9/busra 755 root sys
 f none kernel/misc/sparcv9/cardbus 755 root sys
 f none kernel/misc/sparcv9/cmlb 755 root sys
--- a/usr/src/pkgdefs/SUNWcsl/prototype_com	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_com	Fri Aug 08 11:50:24 2008 -0700
@@ -22,8 +22,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -53,6 +51,8 @@
 d none usr/lib/cfgadm 755 root bin
 f none usr/lib/cfgadm/scsi.so.1 755 root bin
 s none usr/lib/cfgadm/scsi.so=./scsi.so.1
+f none usr/lib/cfgadm/sdcard.so.1 755 root bin
+s none usr/lib/cfgadm/sdcard.so=./sdcard.so.1
 f none usr/lib/cfgadm/pci.so.1 755 root bin
 s none usr/lib/cfgadm/pci.so=./pci.so.1
 f none usr/lib/cfgadm/usb.so.1 755 root bin
--- a/usr/src/pkgdefs/SUNWcsl/prototype_i386	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_i386	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -65,6 +63,8 @@
 d none usr/lib/cfgadm/amd64 755 root bin
 f none usr/lib/cfgadm/amd64/scsi.so.1 755 root bin
 s none usr/lib/cfgadm/amd64/scsi.so=./scsi.so.1
+f none usr/lib/cfgadm/amd64/sdcard.so.1 755 root bin
+s none usr/lib/cfgadm/amd64/sdcard.so=./sdcard.so.1
 f none usr/lib/cfgadm/amd64/pci.so.1 755 root bin
 s none usr/lib/cfgadm/amd64/pci.so=./pci.so.1
 f none usr/lib/cfgadm/amd64/usb.so.1 755 root bin
--- a/usr/src/pkgdefs/SUNWcsl/prototype_sparc	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_sparc	Fri Aug 08 11:50:24 2008 -0700
@@ -24,8 +24,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This required package information file contains a list of package contents.
 # The 'pkgmk' command uses this file to identify the contents of a package
 # and their location on the development machine when building the package.
@@ -61,6 +59,8 @@
 d none usr/lib/cfgadm/sparcv9 755 root bin
 f none usr/lib/cfgadm/sparcv9/scsi.so.1 755 root bin
 s none usr/lib/cfgadm/sparcv9/scsi.so=./scsi.so.1
+f none usr/lib/cfgadm/sparcv9/sdcard.so.1 755 root bin
+s none usr/lib/cfgadm/sparcv9/sdcard.so=./sdcard.so.1
 f none usr/lib/cfgadm/sparcv9/pci.so.1 755 root bin
 s none usr/lib/cfgadm/sparcv9/pci.so=./pci.so.1
 f none usr/lib/cfgadm/sparcv9/usb.so.1 755 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWsdcard/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES) postinstall postremove
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWsdcard/pkginfo.tmpl	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PKG=SUNWsdcard
+NAME=SD/MMC Drivers
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY=system
+VENDOR="Sun Microsystems, Inc."
+DESC="SD/MMC Drivers"
+CLASSES="none preserve"
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWsdcard/postinstall	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,141 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+# Function: check_add_drv()
+#
+# This function will check if the module has an entry in etc/name_to_major
+# If not simply calls add_drv with the arguments given. If there is
+# such an entry in name_to_major file, it adds entries in driver_aliases
+# driver_classes and minor_perm if necessary.
+# The syntax of this function is the same as add_drv. 
+
+check_add_drv()
+{
+	if [ "$BASEDIR" = "" ]
+	then
+		BASEDIR=/  
+	fi
+	alias=""
+	class=""
+	ADD_ALIAS=0
+	ADD_CLASS=0
+	ADD_MINOR=0
+	OPTIND=1
+	IS_NET_DRIVER=0
+
+	cmd="add_drv"
+
+	NO_CMD=
+	while getopts i:b:m:c:N  opt
+	do
+		case $opt in
+			N )	NO_CMD=1;;
+			i )	ADD_ALIAS=1	
+				alias=$OPTARG
+				cmd=$cmd" -i '$alias'"
+				;;
+			m )	ADD_MINOR=1
+				minor=$OPTARG
+				cmd=$cmd" -m '$minor'"
+				;;
+			c)	ADD_CLASS=1
+				class=$OPTARG
+				cmd=$cmd" -c $class"
+				;;
+			b)	BASEDIR=$OPTARG
+				cmd=$cmd" -b $BASEDIR"
+				;;
+			\?) 	echo "check_add_drv can not handle this option"
+				return
+				;;
+			esac
+	done 
+	shift `/usr/bin/expr $OPTIND - 1`
+	
+	drvname=$1
+
+	cmd=$cmd" "$drvname
+
+	drvname=`echo $drvname | /usr/bin/sed 's;.*/;;g'`
+
+	/usr/bin/grep "^$drvname[ 	]" $BASEDIR/etc/name_to_major >  /dev/null 2>&1
+
+	#
+	# NB: We really would have liked to use update_drv here, but 
+	# since we can't do that (see CR 6281386), we have this code.
+	# Note that if we've never added this driver before, the add_drv
+	# below takes care of worrying about conflicting entries in
+	# /etc/driver_aliases.  (It will fail if there is a conflicting
+	# driver squatting on the alias.)
+	# 
+	if [ "$NO_CMD" = "" -a $? -ne 0 ] 
+	then
+		eval $cmd
+	else	
+		# entry already in name_to_major, add alias, class, minorperm
+		# if necessary
+		if [ $ADD_ALIAS = 1 ]	
+		then
+			for i in $alias
+			do
+				/usr/bin/egrep "^$drvname[ 	]+$i" $BASEDIR/etc/driver_aliases>/dev/null 2>&1
+				if [ $? -ne 0 ]
+				then
+					echo "$drvname $i" >> $BASEDIR/etc/driver_aliases	
+				fi
+			done
+		fi
+
+		if [ $ADD_CLASS = 1 ]
+		then
+			/usr/bin/egrep "^$drvname[ 	]+$class( |	|$)" $BASEDIR/etc/driver_classes > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				echo "$drvname\t$class" >> $BASEDIR/etc/driver_classes
+			fi
+		fi
+
+		if [ $ADD_MINOR = 1 ]
+		then
+			/usr/bin/grep "^$drvname:" $BASEDIR/etc/minor_perm > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				minorentry="$drvname:$minor"
+				echo $minorentry >> $BASEDIR/etc/minor_perm
+			fi
+		fi
+
+	fi
+
+
+}
+
+check_add_drv -b "${BASEDIR}" \
+	-i '"pciclass,080500" "pciclass,080501"' \
+	-m '* 0644 root root' sdhost
+
+check_add_drv -b "${BASEDIR}"  \
+	-m '* 0644 root root' sdcard
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWsdcard/postremove	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,44 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+BD=${BASEDIR:-/}
+if grep -w sdcard $BD/etc/name_to_major > /dev/null 2>&1
+then
+	rem_drv -b ${BD} sdcard
+	if [ $? -ne 0 ]
+	then
+		exit 1
+	fi
+fi
+if grep -w sdhost $BD/etc/name_to_major > /dev/null 2>&1
+then
+	rem_drv -b ${BD} sdhost
+	if [ $? -ne 0 ]
+	then
+		exit 1
+	fi
+fi
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWsdcard/prototype_com	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+#
+i pkginfo
+i copyright
+i depend
+i postinstall
+i postremove
+
+# SDcard drivers
+d none kernel			0755	root	sys
+d none kernel/drv		0755	root	sys
+d none kernel/misc		0755	root	sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWsdcard/prototype_i386	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+# SDcard drivers
+f none kernel/drv/sdcard	0755	root	sys
+f none kernel/drv/sdhost	0755	root	sys
+f none kernel/misc/sda		0755	root	sys
+d none kernel/drv/amd64		0755	root	sys
+f none kernel/drv/amd64/sdcard	0755	root	sys
+f none kernel/drv/amd64/sdhost	0755	root	sys
+d none kernel/misc/amd64	0755	root	sys
+f none kernel/misc/amd64/sda	0755	root	sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWsdcard/prototype_sparc	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+# SDcard drivers
+d none kernel/drv/sparcv9		0755	root	sys
+f none kernel/drv/sparcv9/sdcard	0755	root	sys
+f none kernel/drv/sparcv9/sdhost	0755	root	sys
+d none kernel/misc/sparcv9		0755	root	sys
+f none kernel/misc/sparcv9/sda		0755	root	sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES) postinstall postremove depend
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/depend	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,50 @@
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# This package information file defines software dependencies associated
+# with the pkg.  You can define three types of pkg dependencies with this file:
+#	 P indicates a prerequisite for installation
+#	 I indicates an incompatible package
+#	 R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# 	(<arch>)<version>
+# 	(<arch>)<version>
+# 	...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar	Core Architecture, (Root)
+P SUNWcakr	Core Solaris Kernel Architecture (Root)
+P SUNWkvm	Core Architecture, (Kvm)
+P SUNWcsr	Core Solaris, (Root)
+P SUNWckr	Core Solaris Kernel (Root)
+P SUNWcnetr	Core Solaris Network Infrastructure (Root)
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcsd	Core Solaris Devices
+P SUNWcsl	Core Solaris Libraries
+P SUNWsdcard    SD/MMC Drivers
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/pkginfo.tmpl	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PKG=SUNWwbsd
+NAME=Winbond SD Host Driver
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY=system
+VENDOR="Sun Microsystems, Inc."
+DESC="Winbond SD Host Driver"
+CLASSES="none preserve"
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/postinstall	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,139 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+# Function: check_add_drv()
+#
+# This function will check if the module has an entry in etc/name_to_major
+# If not simply calls add_drv with the arguments given. If there is
+# such an entry in name_to_major file, it adds entries in driver_aliases
+# driver_classes and minor_perm if necessary.
+# The syntax of this function is the same as add_drv. 
+
+check_add_drv()
+{
+	if [ "$BASEDIR" = "" ]
+	then
+		BASEDIR=/  
+	fi
+	alias=""
+	class=""
+	ADD_ALIAS=0
+	ADD_CLASS=0
+	ADD_MINOR=0
+	OPTIND=1
+	IS_NET_DRIVER=0
+
+	cmd="add_drv"
+
+	NO_CMD=
+	while getopts i:b:m:c:N  opt
+	do
+		case $opt in
+			N )	NO_CMD=1;;
+			i )	ADD_ALIAS=1	
+				alias=$OPTARG
+				cmd=$cmd" -i '$alias'"
+				;;
+			m )	ADD_MINOR=1
+				minor=$OPTARG
+				cmd=$cmd" -m '$minor'"
+				;;
+			c)	ADD_CLASS=1
+				class=$OPTARG
+				cmd=$cmd" -c $class"
+				;;
+			b)	BASEDIR=$OPTARG
+				cmd=$cmd" -b $BASEDIR"
+				;;
+			\?) 	echo "check_add_drv can not handle this option"
+				return
+				;;
+			esac
+	done 
+	shift `/usr/bin/expr $OPTIND - 1`
+	
+	drvname=$1
+
+	cmd=$cmd" "$drvname
+
+	drvname=`echo $drvname | /usr/bin/sed 's;.*/;;g'`
+
+	/usr/bin/grep "^$drvname[ 	]" $BASEDIR/etc/name_to_major >  /dev/null 2>&1
+
+	#
+	# NB: We really would have liked to use update_drv here, but 
+	# since we can't do that (see CR 6281386), we have this code.
+	# Note that if we've never added this driver before, the add_drv
+	# below takes care of worrying about conflicting entries in
+	# /etc/driver_aliases.  (It will fail if there is a conflicting
+	# driver squatting on the alias.)
+	# 
+	if [ "$NO_CMD" = "" -a $? -ne 0 ] 
+	then
+		eval $cmd
+	else	
+		# entry already in name_to_major, add alias, class, minorperm
+		# if necessary
+		if [ $ADD_ALIAS = 1 ]	
+		then
+			for i in $alias
+			do
+				/usr/bin/egrep "^$drvname[ 	]+$i" $BASEDIR/etc/driver_aliases>/dev/null 2>&1
+				if [ $? -ne 0 ]
+				then
+					echo "$drvname $i" >> $BASEDIR/etc/driver_aliases	
+				fi
+			done
+		fi
+
+		if [ $ADD_CLASS = 1 ]
+		then
+			/usr/bin/egrep "^$drvname[ 	]+$class( |	|$)" $BASEDIR/etc/driver_classes > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				echo "$drvname\t$class" >> $BASEDIR/etc/driver_classes
+			fi
+		fi
+
+		if [ $ADD_MINOR = 1 ]
+		then
+			/usr/bin/grep "^$drvname:" $BASEDIR/etc/minor_perm > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				minorentry="$drvname:$minor"
+				echo $minorentry >> $BASEDIR/etc/minor_perm
+			fi
+		fi
+
+	fi
+
+
+}
+
+check_add_drv -b "${BASEDIR}" \
+	-i '"TAD,wb-sdcard" \
+	-m '* 0644 root root' wbsd
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/postremove	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,36 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+BD=${BASEDIR:-/}
+if grep -w wbsd $BD/etc/name_to_major > /dev/null 2>&1
+then
+	rem_drv -b ${BD} wbsd
+	if [ $? -ne 0 ]
+	then
+		exit 1
+	fi
+fi
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/prototype_com	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+#
+i pkginfo
+i copyright
+i depend
+i postinstall
+i postremove
+
+# Winbond SD driver
+d none kernel			0755	root	sys
+d none kernel/drv		0755	root	sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/prototype_i386	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+# Winbond SD driver
+# Not delivered on Intel - yet!
+#f none kernel/drv/wbsd		0755	root	sys
+#d none kernel/drv/amd64	0755	root	sys
+#f none kernel/drv/amd64/wbsd	0755	root	sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWwbsd/prototype_sparc	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+# Winbond SD driver
+d none kernel/drv/sparcv9		0755	root	sys
+f none kernel/drv/sparcv9/wbsd		0755	root	sys
--- a/usr/src/pkgdefs/etc/exception_list_i386	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/etc/exception_list_i386	Fri Aug 08 11:50:24 2008 -0700
@@ -22,8 +22,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # Exception List for protocmp
 #
 ###########################################
@@ -1077,6 +1075,13 @@
 # directory is created in the proto area to keep other tools happy.
 usr/platform/i86hvm		i386
 #
+# Private sdcard framework headers
+#
+usr/include/sys/sdcard			i386
+usr/include/sys/sdcard/sda.h		i386
+usr/include/sys/sdcard/sda_impl.h	i386
+usr/include/sys/sdcard/sda_ioctl.h	i386
+#
 # libsmbfs is private
 #
 usr/lib/libsmbfs.so			i386
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Fri Aug 08 11:50:24 2008 -0700
@@ -22,8 +22,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # Exception List for protocmp
 #
 ###########################################
@@ -1157,6 +1155,13 @@
 usr/lib/vscan/llib-lvscan	sparc
 usr/lib/vscan/llib-lvscan.ln	sparc
 #
+# Private sdcard framework headers
+#
+usr/include/sys/sdcard			sparc
+usr/include/sys/sdcard/sda.h		sparc
+usr/include/sys/sdcard/sda_impl.h	sparc
+usr/include/sys/sdcard/sda_ioctl.h	sparc
+#
 # libsmbfs is private
 #
 usr/lib/libsmbfs.so			sparc
--- a/usr/src/uts/common/Makefile.files	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/common/Makefile.files	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This Makefile defines all file modules for the directory uts/common
 # and its children. These are the source files which may be considered
 # common to all SunOS systems.
@@ -1617,6 +1615,11 @@
 #
 KICONV_EMEA_OBJS += kiconv_emea.o
 
+#
+#	blk2scsa
+#
+BLK2SCSA_OBJS = blk2scsa.o
+
 KICONV_JA_OBJS += kiconv_ja.o
 
 KICONV_KO_OBJS += kiconv_cck_common.o kiconv_ko.o
@@ -1629,6 +1632,16 @@
 #	AAC module
 #
 AAC_OBJS = aac.o aac_ioctl.o
+
+#
+#	sdcard modules
+#
+SDA_OBJS =	sda_cmd.o sda_host.o sda_init.o sda_mem.o sda_mod.o \
+		sda_nexus.o sda_slot.o
+SDCARD_OBJS =	sdcard.o
+SDHOST_OBJS =	sdhost.o
+WBSD_OBJS =	wbsd.o
+
 #
 #	hxge 10G driver module
 #
--- a/usr/src/uts/common/Makefile.rules	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/common/Makefile.rules	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # uts/common/Makefile.rules
 #
 #	This Makefile defines all the file build rules for the directory
@@ -763,7 +761,7 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
-$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/softmac/%.c
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/scsi/adapters/blk2scsa/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
@@ -775,10 +773,30 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/sdcard/adapters/sdhost/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/sdcard/adapters/wbsd/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/sdcard/impl/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/sdcard/targets/sdcard/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/sfe/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/softmac/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:                $(UTSBASE)/common/io/ural/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1642,6 +1660,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/blk2scsa/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/scsi_vhci/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
@@ -1657,12 +1678,24 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/targets/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
-$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/softmac/%.c
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/sdcard/adapters/sdhost/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/sdcard/adapters/wbsd/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/sdcard/impl/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/sdcard/targets/sdcard/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
 $(LINTS_DIR)/%.ln:              $(UTSBASE)/common/io/sfe/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/softmac/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:              $(UTSBASE)/common/io/ural/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/blk2scsa/blk2scsa.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,1971 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/ksynch.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/adapters/blk2scsa.h>
+
+/*
+ * We implement the following SCSI-2 commands on behalf of targets:
+ *
+ * SCMD_DOORLOCK
+ * SCMD_FORMAT
+ * SCMD_INQUIRY
+ * SCMD_MODE_SENSE
+ * SCMD_READ
+ * SCMD_READ_G1
+ * SCMD_READ_CAPACITY
+ * SCMD_RELEASE
+ * SCMD_REQUEST_SENSE
+ * SCMD_RESERVE
+ * SCMD_SDIAG
+ * SCMD_START_STOP
+ * SCMD_TEST_UNIT_READY
+ * SCMD_WRITE
+ * SCMD_WRITE_G1
+ *
+ * We really should, at some point in the future, investigate offering
+ * more complete SCSI-3 commands, including the G4 and G5 variants of
+ * READ and WRITE, MODE_SELECT, PERSISTENT_RESERVE_IN,
+ * PERSISTENT_RESERVE_OUT, SYNCHRONIZE_CACHE, READ_MEDIAL_SERIAL,
+ * REPORT_LUNS, etc.
+ */
+
+typedef struct b2s_request_impl b2s_request_impl_t;
+
+struct b2s_request_impl {
+	b2s_request_t		ri_public;
+	struct scsi_pkt		*ri_pkt;
+	struct scsi_arq_status	*ri_sts;
+	buf_t			*ri_bp;
+
+	size_t			ri_resid;
+	b2s_nexus_t		*ri_nexus;
+	b2s_leaf_t		*ri_leaf;
+	void			(*ri_done)(struct b2s_request_impl *);
+};
+
+#define	ri_lun		ri_public.br_lun
+#define	ri_target	ri_public.br_target
+#define	ri_cmd		ri_public.br_cmd
+#define	ri_errno	ri_public.br_errno
+#define	ri_count	ri_public.br_count
+#define	ri_xfered	ri_public.br_xfered
+
+#define	ri_flags	ri_public.br_flags
+#define	ri_media	ri_public.br_media
+#define	ri_inquiry	ri_public.br_inquiry
+#define	ri_lba		ri_public.br_lba
+#define	ri_nblks	ri_public.br_nblks
+
+struct b2s_nexus {
+	dev_info_t		*n_dip;
+	struct scsi_hba_tran	*n_tran;
+	void			*n_private;
+	ddi_dma_attr_t		*n_dma;
+	boolean_t		(*n_request)(void *, b2s_request_t *);
+
+	kmutex_t		n_lock;
+	kcondvar_t		n_cv;
+	boolean_t		n_attached;
+	list_t			n_leaves;
+};
+#define	B2S_NEXUS_ATTACHED	(1U << 0)
+
+_NOTE(MUTEX_PROTECTS_DATA(b2s_nexus::n_lock, b2s_nexus::n_leaves))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", b2s_nexus::n_dip))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", b2s_nexus::n_private))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", b2s_nexus::n_request))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", b2s_nexus::n_dma))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", b2s_nexus::n_tran))
+_NOTE(SCHEME_PROTECTS_DATA("client synchronized", b2s_nexus::n_attached))
+
+struct b2s_leaf {
+	b2s_nexus_t		*l_nexus;
+	uint_t			l_target;
+	uint_t			l_lun;
+	uint32_t		l_flags;
+	char			*l_uuid;
+	uint32_t		l_refcnt;
+	list_node_t		l_node;
+	struct scsi_inquiry	l_inq;
+};
+
+_NOTE(MUTEX_PROTECTS_DATA(b2s_nexus::n_lock, b2s_leaf::l_node))
+_NOTE(MUTEX_PROTECTS_DATA(b2s_nexus::n_lock, b2s_leaf::l_refcnt))
+_NOTE(MUTEX_PROTECTS_DATA(b2s_nexus::n_lock, b2s_leaf::l_uuid))
+_NOTE(MUTEX_PROTECTS_DATA(b2s_nexus::n_lock, b2s_leaf::l_lun))
+_NOTE(MUTEX_PROTECTS_DATA(b2s_nexus::n_lock, b2s_leaf::l_target))
+_NOTE(MUTEX_PROTECTS_DATA(b2s_nexus::n_lock, b2s_leaf::l_nexus))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(b2s_leaf::l_uuid))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(b2s_leaf::l_lun))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(b2s_leaf::l_target))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(b2s_leaf::l_nexus))
+
+_NOTE(SCHEME_PROTECTS_DATA("stable data", scsi_hba_tran))
+_NOTE(SCHEME_PROTECTS_DATA("unshared data", b2s_request_impl))
+_NOTE(SCHEME_PROTECTS_DATA("unique per packet", scsi_arq_status))
+_NOTE(SCHEME_PROTECTS_DATA("unique per packet", scsi_pkt))
+_NOTE(SCHEME_PROTECTS_DATA("unique per packet", scsi_inquiry))
+_NOTE(SCHEME_PROTECTS_DATA("client synchronized", b2s_leaf::l_flags))
+
+/*
+ * This copies a string into a target buf, obeying the size limits
+ * of the target.  It does not null terminate, ever.
+ */
+#define	COPYSTR(src, dst)	bcopy(src, dst, min(strlen(src), sizeof (dst)))
+
+/*
+ * Thank you SCSA, for making it a PITA to deal with a single byte
+ * value by turning it into a bitfield!
+ */
+#define	PUTSTAT(dst, val)	(*((uint8_t *)(void *)&dst) = val)
+
+struct b2s_error {
+	uint8_t			e_reason;	/* scsi CMD_xxx reason */
+	uint8_t			e_status;	/* scsi STATUS_xxx code */
+	uint8_t			e_skey;		/* sense key */
+	uint8_t			e_asc;		/* additional sense code */
+	uint8_t			e_ascq;		/* sense code qualifier */
+	uint8_t			e_sksv[3];	/* sense key specific-value */
+};
+
+static struct b2s_error b2s_errs[B2S_NERRS];
+
+static struct modlmisc modlmisc = {
+	&mod_miscops,
+	"SCSA Block Device Emulation",
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, { &modlmisc, NULL }
+};
+
+/*
+ * For layers that don't provide a DMA attribute, we offer a default
+ * one.  Such devices probably just want to do mapin, all of the time,
+ * but since SCSI doesn't give us a way to indicate that, we have to
+ * provide a fake attribute.  Slightly wasteful, but PIO-only disk
+ * devices are going to have some performance issues anyway.
+ *
+ * For such devices, we only want to commit to transferring 64K at a time,
+ * and let the SCSA layer break it up for us.
+ */
+static struct ddi_dma_attr b2s_default_dma_attr =  {
+	DMA_ATTR_V0,
+	0,			/* lo address */
+	0xffffffffffffffffULL,	/* high address */
+	0xffffU,		/* DMA counter max */
+	1,			/* alignment */
+	0x0c,			/* burst sizes */
+	1,			/* minimum transfer size */
+	0xffffU,		/* maximum transfer size */
+	0xffffU,		/* maximum segment size */
+	1,			/* scatter/gather list length */
+	1,			/* granularity */
+	0			/* DMA flags */
+};
+
+
+/*
+ * Private prototypes.
+ */
+
+static int b2s_tran_tgt_init(dev_info_t *, dev_info_t *,
+    scsi_hba_tran_t *, struct scsi_device *);
+static void b2s_tran_tgt_free(dev_info_t *, dev_info_t *,
+    scsi_hba_tran_t *, struct scsi_device *);
+static int b2s_tran_getcap(struct scsi_address *, char *, int);
+static int b2s_tran_setcap(struct scsi_address *, char *, int, int);
+static void b2s_tran_teardown_pkt(struct scsi_pkt *);
+static int b2s_tran_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t);
+static int b2s_tran_start(struct scsi_address *, struct scsi_pkt *);
+static int b2s_tran_abort(struct scsi_address *, struct scsi_pkt *);
+static int b2s_tran_reset(struct scsi_address *, int);
+static int b2s_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *,
+    dev_info_t **);
+static b2s_leaf_t *b2s_hold_leaf(b2s_nexus_t *, uint_t, uint_t);
+static dev_info_t *b2s_find_node(b2s_nexus_t *, b2s_leaf_t *);
+static int b2s_create_node(b2s_nexus_t *, b2s_leaf_t *, dev_info_t **);
+static int b2s_update_props(dev_info_t *, b2s_leaf_t *, char **, int);
+static void b2s_inquiry_done(b2s_request_impl_t *);
+static int b2s_inquiry(b2s_leaf_t *);
+static void b2s_init_err_table(void);
+static int b2s_scmd_inq(b2s_request_impl_t *);
+static int b2s_scmd_tur(b2s_request_impl_t *);
+static int b2s_scmd_doorlock(b2s_request_impl_t *);
+static int b2s_scmd_format(b2s_request_impl_t *);
+static int b2s_scmd_readcap(b2s_request_impl_t *);
+static int b2s_scmd_rw(b2s_request_impl_t *);
+static int b2s_scmd_rqs(b2s_request_impl_t *);
+static int b2s_scmd_sdiag(b2s_request_impl_t *);
+static int b2s_scmd_start_stop(b2s_request_impl_t *);
+static int b2s_scmd_mode_sense(b2s_request_impl_t *);
+static int b2s_scmd_reserve_release(b2s_request_impl_t *);
+static void b2s_scmd_readcap_done(b2s_request_impl_t *);
+static void b2s_scmd_mode_sense_done(b2s_request_impl_t *);
+static void b2s_warn(b2s_leaf_t *, const char *, ...);
+
+int
+_init(void)
+{
+	int	rv;
+
+	b2s_init_err_table();
+	rv = mod_install(&modlinkage);
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int	rv;
+
+	rv = mod_remove(&modlinkage);
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+b2s_mod_init(struct modlinkage *modlp)
+{
+	return (scsi_hba_init(modlp));
+}
+
+void
+b2s_mod_fini(struct modlinkage *modlp)
+{
+	scsi_hba_fini(modlp);
+}
+
+void
+b2s_init_err_table(void)
+{
+	int	i;
+
+	/* fill up most of them with defaults */
+	for (i = 0; i < B2S_NERRS; i++) {
+		b2s_errs[i].e_reason = CMD_CMPLT;
+		b2s_errs[i].e_status = STATUS_CHECK;
+		b2s_errs[i].e_skey = KEY_NO_SENSE;
+		b2s_errs[i].e_asc = 0;
+		b2s_errs[i].e_ascq = 0;
+		b2s_errs[i].e_sksv[0] = 0;
+		b2s_errs[i].e_sksv[1] = 0;
+		b2s_errs[i].e_sksv[2] = 0;
+	}
+
+	/* now flesh out real values */
+	b2s_errs[B2S_EOK].e_status = STATUS_GOOD;
+
+	b2s_errs[B2S_ENOTSUP].e_skey = KEY_ILLEGAL_REQUEST;
+	b2s_errs[B2S_ENOTSUP].e_asc = 0x20;
+
+	b2s_errs[B2S_EFORMATTING].e_skey = KEY_NOT_READY;
+	b2s_errs[B2S_EFORMATTING].e_asc = 0x04;
+	b2s_errs[B2S_EFORMATTING].e_ascq = 0x04;
+	b2s_errs[B2S_EFORMATTING].e_sksv[0] = 0x80;
+
+	b2s_errs[B2S_ENOMEDIA].e_skey = KEY_NOT_READY;
+	b2s_errs[B2S_ENOMEDIA].e_asc = 0x3A;
+
+	b2s_errs[B2S_EMEDIACHG].e_skey = KEY_UNIT_ATTENTION;
+	b2s_errs[B2S_EMEDIACHG].e_asc = 0x28;
+
+	b2s_errs[B2S_ESTOPPED].e_skey = KEY_NOT_READY;
+	b2s_errs[B2S_ESTOPPED].e_asc = 0x04;
+	b2s_errs[B2S_ESTOPPED].e_ascq = 0x02;
+
+	b2s_errs[B2S_EBLKADDR].e_skey = KEY_ILLEGAL_REQUEST;
+	b2s_errs[B2S_EBLKADDR].e_asc = 0x21;
+
+	b2s_errs[B2S_EIO].e_skey = KEY_HARDWARE_ERROR;
+	b2s_errs[B2S_EIO].e_asc = 0x08;
+	b2s_errs[B2S_EIO].e_ascq = 0x00;
+
+	b2s_errs[B2S_EHARDWARE].e_skey = KEY_HARDWARE_ERROR;
+	b2s_errs[B2S_EHARDWARE].e_asc = 0x44;
+
+	b2s_errs[B2S_ENODEV].e_reason = CMD_DEV_GONE;
+
+	b2s_errs[B2S_EMEDIA].e_skey = KEY_MEDIUM_ERROR;
+
+	b2s_errs[B2S_EDOORLOCK].e_skey = KEY_NOT_READY;
+	b2s_errs[B2S_EDOORLOCK].e_asc = 0x53;
+	b2s_errs[B2S_EDOORLOCK].e_ascq = 0x02;
+
+	b2s_errs[B2S_EWPROTECT].e_skey = KEY_DATA_PROTECT;
+	b2s_errs[B2S_EWPROTECT].e_asc = 0x27;
+
+	b2s_errs[B2S_ESTARTING].e_skey = KEY_NOT_READY;
+	b2s_errs[B2S_ESTARTING].e_asc = 0x04;
+	b2s_errs[B2S_ESTARTING].e_ascq = 0x01;
+
+	b2s_errs[B2S_ETIMEDOUT].e_skey = KEY_ABORTED_COMMAND;
+	b2s_errs[B2S_ETIMEDOUT].e_asc = 0x08;
+	b2s_errs[B2S_ETIMEDOUT].e_ascq = 0x01;
+
+	/*
+	 * This one, SYSTEM_RESOURCE_FAILURE, is not really legal for
+	 * DTYPE_DIRECT in SCSI-2, but sd doesn't care, and reporting
+	 * it this way may help diagnosis.  sd will retry it in any
+	 * case.
+	 */
+	b2s_errs[B2S_ENOMEM].e_skey = KEY_ABORTED_COMMAND;
+	b2s_errs[B2S_ENOMEM].e_asc = 0x55;
+
+	b2s_errs[B2S_ERESET].e_reason = CMD_RESET;
+
+	b2s_errs[B2S_EABORT].e_reason = CMD_ABORTED;
+
+	b2s_errs[B2S_ERSVD].e_status = STATUS_RESERVATION_CONFLICT;
+
+	b2s_errs[B2S_EINVAL].e_skey = KEY_ILLEGAL_REQUEST;
+	b2s_errs[B2S_EINVAL].e_asc = 0x24;
+
+	b2s_errs[B2S_EPARAM].e_skey = KEY_ILLEGAL_REQUEST;
+	b2s_errs[B2S_EPARAM].e_asc = 0x26;
+
+	b2s_errs[B2S_EBADMSG].e_reason = CMD_BADMSG;
+}
+
+/*
+ * Locate the the leaf node for the given target/lun.  This must be
+ * called with the nexus lock held.
+ */
+b2s_leaf_t *
+b2s_get_leaf(b2s_nexus_t *n, uint_t target, uint_t lun)
+{
+	b2s_leaf_t *l;
+
+	ASSERT(mutex_owned(&n->n_lock));
+
+	l = list_head(&n->n_leaves);
+	while (l != NULL) {
+		ASSERT(l->l_nexus == n);
+		if ((l->l_target == target) && (l->l_lun == lun)) {
+			break;
+		}
+		l = list_next(&n->n_leaves, l);
+	}
+
+	return (l);
+}
+
+/*
+ * Locate the the leaf node for the given target/lun, and hold it.  The
+ * nexus lock must *NOT* be held.
+ */
+b2s_leaf_t *
+b2s_hold_leaf(b2s_nexus_t *n, uint_t target, uint_t lun)
+{
+	b2s_leaf_t	*l;
+
+	mutex_enter(&n->n_lock);
+	l = b2s_get_leaf(n, target, lun);
+	if (l != NULL) {
+		l->l_refcnt++;
+	}
+	mutex_exit(&n->n_lock);
+	return (l);
+}
+
+/*
+ * Drop the hold on the leaf.
+ */
+void
+b2s_rele_leaf(b2s_leaf_t *l)
+{
+	b2s_nexus_t *n = l->l_nexus;
+	mutex_enter(&n->n_lock);
+	l->l_refcnt--;
+	if (l->l_refcnt == 0) {
+		list_remove(&n->n_leaves, l);
+		kmem_free(l->l_uuid, strlen(l->l_uuid) + 1);
+		kmem_free(l, sizeof (*l));
+	}
+	mutex_exit(&n->n_lock);
+}
+
+/*
+ * This is used to walk the list of leaves safely, without requiring the
+ * nexus lock to be held.  The returned leaf is held.  (If the passed in
+ * lastl is not NULL, then it is released as well.)
+ *
+ * Pass NULL for lastl to start the walk.
+ */
+b2s_leaf_t *
+b2s_next_leaf(b2s_nexus_t *n, b2s_leaf_t *lastl)
+{
+	b2s_leaf_t *l;
+
+	mutex_enter(&n->n_lock);
+	if (lastl == NULL) {
+		l = list_head(&n->n_leaves);
+	} else {
+		l = list_next(&n->n_leaves, lastl);
+	}
+	if (l != NULL) {
+		l->l_refcnt++;
+	}
+	mutex_exit(&n->n_lock);
+
+	if (lastl != NULL) {
+		b2s_rele_leaf(lastl);
+	}
+
+	return (l);
+}
+
+void
+b2s_request_mapin(b2s_request_t *req, caddr_t *addrp, size_t *lenp)
+{
+	b2s_request_impl_t	 *ri = (void *)req;
+	struct scsi_pkt		*pkt = ri->ri_pkt;
+	buf_t			*bp;
+
+	if ((pkt != NULL) && ((bp = scsi_pkt2bp(pkt)) != NULL) &&
+	    (bp->b_bcount != 0)) {
+
+		/*
+		 * This uses some undocumented fields of the SCSI
+		 * packet, but it is what is required to get the
+		 * kernel virtual addresses.
+		 */
+
+		ri->ri_bp = bioclone(bp, pkt->pkt_dma_offset,
+		    pkt->pkt_dma_len, bp->b_edev, bp->b_blkno, NULL,
+		    ri->ri_bp, KM_SLEEP);
+		if ((bp->b_flags & (B_PAGEIO | B_PHYS)) != 0) {
+			ri->ri_flags |= B2S_REQUEST_FLAG_MAPIN;
+			bp_mapin(ri->ri_bp);
+		}
+
+		*addrp = ri->ri_bp->b_un.b_addr;
+		*lenp = pkt->pkt_dma_len;
+	} else {
+		*addrp = 0;
+		*lenp = 0;
+	}
+}
+
+void
+b2s_request_dma(b2s_request_t *req, uint_t *ndmacp, ddi_dma_cookie_t **dmacsp)
+{
+	b2s_request_impl_t	*ri = (void *)req;
+	struct scsi_pkt		*pkt = ri->ri_pkt;
+
+	*ndmacp = pkt->pkt_numcookies;
+	*dmacsp = pkt->pkt_cookies;
+}
+
+void
+b2s_request_done_pkt(b2s_request_impl_t *ri)
+{
+	struct scsi_pkt		*pkt;
+	uint8_t			status;
+	struct scsi_arq_status	*sts = ri->ri_sts;
+	b2s_err_t		err;
+
+	err = ri->ri_errno;
+
+	pkt = ri->ri_pkt;
+	pkt->pkt_resid = ri->ri_resid;
+
+	bzero(sts, sizeof (*sts));
+
+	/*
+	 * Make sure that the status is in range of our known errs.  If we
+	 * don't know it, then just cobble up a bogus one.
+	 */
+	if ((err < 0) || (err >= B2S_NERRS)) {
+		pkt->pkt_reason = CMD_TRAN_ERR;
+	} else {
+		pkt->pkt_reason = b2s_errs[err].e_reason;
+		status = b2s_errs[err].e_status;
+	}
+
+	if (pkt->pkt_reason == CMD_CMPLT) {
+
+		pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+		    STATE_SENT_CMD | STATE_GOT_STATUS;
+
+		PUTSTAT(sts->sts_status, status);
+
+		if (status == STATUS_CHECK) {
+			/*
+			 * Contingent allegiance.  We need to do the
+			 * ARQ thing.
+			 */
+			PUTSTAT(sts->sts_rqpkt_status, STATUS_GOOD);
+
+			sts->sts_rqpkt_reason = CMD_CMPLT;
+			sts->sts_rqpkt_resid = 0;
+			sts->sts_rqpkt_state = STATE_XFERRED_DATA |
+			    STATE_GOT_BUS | STATE_GOT_STATUS;
+
+			sts->sts_sensedata.es_valid = 1;
+			sts->sts_sensedata.es_class = CLASS_EXTENDED_SENSE;
+			sts->sts_sensedata.es_key = b2s_errs[err].e_skey;
+			sts->sts_sensedata.es_add_code = b2s_errs[err].e_asc;
+			sts->sts_sensedata.es_qual_code = b2s_errs[err].e_ascq;
+			bcopy(sts->sts_sensedata.es_skey_specific,
+			    b2s_errs[err].e_sksv, 3);
+			/*
+			 * Stash any residue information.
+			 */
+			sts->sts_sensedata.es_info_1 =
+			    (ri->ri_resid >> 24) & 0xff;
+			sts->sts_sensedata.es_info_2 =
+			    (ri->ri_resid >> 16) & 0xff;
+			sts->sts_sensedata.es_info_3 =
+			    (ri->ri_resid >> 8) & 0xff;
+			sts->sts_sensedata.es_info_4 =
+			    (ri->ri_resid) & 0xff;
+
+			pkt->pkt_state |= STATE_ARQ_DONE;
+		}
+
+	} else if (pkt->pkt_reason == CMD_ABORTED) {
+		pkt->pkt_statistics |= STAT_ABORTED;
+	} else if (pkt->pkt_reason == CMD_RESET) {
+		pkt->pkt_statistics |= STAT_DEV_RESET;
+	} else {
+		pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
+		    STATE_SENT_CMD;
+	}
+
+	/*
+	 * N.B.: Obviously not all commands actually have a SCSI
+	 * DATA-IN or DATA-OUT phase.  But it doesn't matter, since
+	 * sd.c only bothers to look at this flag for request sense
+	 * traffic, which is always correct within our emulation.
+	 *
+	 * We go ahead and set it on all good packets however, since
+	 * there may in the future be some additional checks to make
+	 * sure a data transfer occurred.  This seems safer (since
+	 * then sd should examine pkt_resid) rather than leaving it
+	 * off by default.
+	 */
+	if (ri->ri_errno == 0) {
+		pkt->pkt_state |= STATE_XFERRED_DATA;
+	}
+
+	/*
+	 * Finally, execute the callback (unless running POLLED)
+	 */
+	if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && (pkt->pkt_comp != NULL)) {
+		pkt->pkt_comp(pkt);
+	}
+
+}
+
+void
+b2s_request_done(b2s_request_t *req, b2s_err_t err, size_t resid)
+{
+	b2s_request_impl_t	*ri = (void *)req;
+
+	ri->ri_errno = err;
+	ri->ri_resid = (ssize_t)resid;
+
+	/*
+	 * Post process...  this is used for massaging results into
+	 * what SCSI wants.
+	 */
+	if (ri->ri_done != NULL)
+		ri->ri_done(ri);
+
+	/*
+	 * Undo the effect of any specific mapin that may have been done to
+	 * process the request.
+	 */
+	if (ri->ri_flags & B2S_REQUEST_FLAG_MAPIN) {
+		bp_mapout(ri->ri_bp);
+		ri->ri_flags &= ~B2S_REQUEST_FLAG_MAPIN;
+	}
+
+	/*
+	 * For SCSI packets, we have special completion handling.  For
+	 * internal requests, we just mark the request done so the caller
+	 * can free it.
+	 */
+	if (ri->ri_pkt == NULL) {
+		b2s_nexus_t	*n = ri->ri_nexus;
+
+		mutex_enter(&n->n_lock);
+		ri->ri_flags |= B2S_REQUEST_FLAG_DONE;
+		cv_broadcast(&n->n_cv);
+		mutex_exit(&n->n_lock);
+	} else {
+		b2s_request_done_pkt(ri);
+	}
+}
+
+/*ARGSUSED*/
+int
+b2s_tran_tgt_init(dev_info_t *hbadip, dev_info_t *tgtdip,
+    scsi_hba_tran_t *tran, struct scsi_device *sd)
+{
+	uint_t		tgt, lun;
+	b2s_nexus_t	*n;
+	b2s_leaf_t	*l;
+
+	/*
+	 * Lookup the target and lun.
+	 */
+	tgt = (uint_t)ddi_prop_get_int(DDI_DEV_T_ANY, tgtdip,
+	    DDI_PROP_DONTPASS, "target", -1);
+
+	lun = (uint_t)ddi_prop_get_int(DDI_DEV_T_ANY, tgtdip,
+	    DDI_PROP_DONTPASS, "lun", -1);
+
+	n = tran->tran_hba_private;
+
+	/*
+	 * Hold the leaf node as long as the devinfo node is using it.
+	 */
+	l = b2s_hold_leaf(n, tgt, lun);
+	if (l == NULL) {
+		/*
+		 * Target node not found on bus.
+		 */
+		return (DDI_FAILURE);
+	}
+	tran->tran_tgt_private = l;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+void
+b2s_tran_tgt_free(dev_info_t *hbadip, dev_info_t *tgtdip,
+    scsi_hba_tran_t *tran, struct scsi_device *sd)
+{
+	b2s_leaf_t	*l;
+
+	l = tran->tran_tgt_private;
+	ASSERT(l != NULL);
+	b2s_rele_leaf(l);
+}
+
+/*ARGSUSED*/
+int
+b2s_tran_setup_pkt(struct scsi_pkt *pkt, int (*cb)(caddr_t), caddr_t arg)
+{
+	b2s_request_impl_t *ri = pkt->pkt_ha_private;
+
+	ri->ri_pkt = pkt;
+	ri->ri_sts = (struct scsi_arq_status *)(void *)pkt->pkt_scbp;
+	ri->ri_bp = getrbuf(KM_SLEEP);
+
+	/*
+	 * NB: The other fields are initialized properly at tran_start(9e).
+	 * We don't care about their values the rest of the time.
+	 */
+	return (0);
+}
+
+/*ARGSUSED*/
+void
+b2s_tran_teardown_pkt(struct scsi_pkt *pkt)
+{
+	b2s_request_impl_t *ri = pkt->pkt_ha_private;
+
+	/*
+	 * Free the cloned buf header.
+	 */
+	freerbuf(ri->ri_bp);
+}
+
+/*ARGSUSED*/
+int
+b2s_tran_getcap(struct scsi_address *ap, char *cap, int whom)
+{
+	int	capid;
+
+	capid = scsi_hba_lookup_capstr(cap);
+
+	switch (capid) {
+	case SCSI_CAP_ARQ:
+	case SCSI_CAP_UNTAGGED_QING:
+		return (1);
+
+	default:
+		return (-1);
+	}
+}
+
+int
+b2s_tran_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	b2s_request_impl_t	*ri;
+	b2s_nexus_t		*n = ap->a_hba_tran->tran_hba_private;
+	b2s_leaf_t		*l = ap->a_hba_tran->tran_tgt_private;
+	int			err;
+
+	/*
+	 * We can only do the blind abort of all packets.  We have
+	 * no way to request an individual packet be aborted.
+	 */
+	if (pkt != NULL) {
+		return (B_FALSE);
+	}
+
+	ri = kmem_zalloc(sizeof (*ri), KM_NOSLEEP);
+	if (ri == NULL) {
+		return (B_FALSE);
+	}
+	ri->ri_cmd = B2S_CMD_ABORT;
+	ri->ri_target = l->l_target;
+	ri->ri_lun = l->l_lun;
+	ri->ri_flags = B2S_REQUEST_FLAG_HEAD;
+	ri->ri_leaf = l;
+	ri->ri_nexus = n;
+	/* leave all else null */
+
+	/*
+	 * Submit request to device driver.
+	 */
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		/* this shouldn't happen, since we are just starting out */
+		b2s_warn(l, "Busy trying to abort");
+		kmem_free(ri, sizeof (*ri));
+		return (B_FALSE);
+	}
+
+	/*
+	 * Wait for command completion.
+	 */
+	mutex_enter(&n->n_lock);
+	while ((ri->ri_flags & B2S_REQUEST_FLAG_DONE) == 0)
+		cv_wait(&n->n_cv, &n->n_lock);
+	mutex_exit(&n->n_lock);
+
+	err = ri->ri_errno;
+	kmem_free(ri, sizeof (*ri));
+
+	if (err != 0) {
+		b2s_warn(l, "Failed during abort (error %d)", err);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+int
+b2s_tran_reset(struct scsi_address *ap, int level)
+{
+	b2s_request_impl_t	*ri;
+	b2s_nexus_t		*n = ap->a_hba_tran->tran_hba_private;
+	b2s_leaf_t		*l = ap->a_hba_tran->tran_tgt_private;
+	int			err;
+
+	if (level == RESET_LUN) {
+		return (B_FALSE);
+	}
+
+	ri = kmem_zalloc(sizeof (*ri), KM_NOSLEEP);
+	if (ri == NULL) {
+		return (B_FALSE);
+	}
+	ri->ri_cmd = B2S_CMD_RESET;
+	ri->ri_target = l->l_target;
+	ri->ri_lun = l->l_lun;
+	ri->ri_flags = B2S_REQUEST_FLAG_HEAD;
+	ri->ri_leaf = l;
+	ri->ri_nexus = n;
+	/* leave all else null */
+
+	/*
+	 * Submit request to device driver.
+	 */
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		/* this shouldn't happen, since we are just starting out */
+		b2s_warn(l, "Busy trying to reset");
+		kmem_free(ri, sizeof (*ri));
+		return (B_FALSE);
+	}
+
+	/*
+	 * Wait for command completion.
+	 */
+	mutex_enter(&n->n_lock);
+	while ((ri->ri_flags & B2S_REQUEST_FLAG_DONE) == 0)
+		cv_wait(&n->n_cv, &n->n_lock);
+	mutex_exit(&n->n_lock);
+
+	err = ri->ri_errno;
+	kmem_free(ri, sizeof (*ri));
+
+	if (err != 0) {
+		b2s_warn(l, "Failed during reset (error %d)", err);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*ARGSUSED*/
+int
+b2s_tran_setcap(struct scsi_address *ap, char *cap, int val, int whom)
+{
+	int	capid;
+
+	capid = scsi_hba_lookup_capstr(cap);
+
+	switch (capid) {
+	case SCSI_CAP_ARQ:
+		if (val == 0) {
+			return (0);
+		} else {
+			return (1);
+		}
+
+	default:
+		return (-1);
+	}
+}
+
+int
+b2s_tran_start(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	b2s_request_impl_t	*ri = pkt->pkt_ha_private;
+	b2s_nexus_t		*n = ap->a_hba_tran->tran_hba_private;
+	b2s_leaf_t		*l = ap->a_hba_tran->tran_tgt_private;
+
+	ri->ri_errno = B2S_EOK;
+	ri->ri_resid = 0;
+	ri->ri_public.br_args.a_ints[0] = 0;
+	ri->ri_public.br_args.a_ints[1] = 0;
+	ri->ri_flags = 0;
+	ri->ri_done = NULL;
+
+	if ((n == NULL) || (l == NULL) ||
+	    ((l->l_flags & B2S_LEAF_DETACHED) != 0)) {
+		/*
+		 * Leaf is not on the bus!
+		 *
+		 * We should add support for inquiry when lun != 0,
+		 * even if when the lun does not exist, but lun 0 is
+		 * present.  But, it turns out this is not strictly
+		 * required by sd(7d).
+		 */
+		b2s_request_done(&ri->ri_public, B2S_ENODEV, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	ri->ri_nexus = n;
+	ri->ri_leaf = l;
+	ri->ri_target = l->l_target;
+	ri->ri_lun = l->l_lun;
+
+	if (pkt->pkt_flags & FLAG_NOINTR)
+		ri->ri_flags |= B2S_REQUEST_FLAG_POLL;
+	if (pkt->pkt_flags & FLAG_HEAD)
+		ri->ri_flags |= B2S_REQUEST_FLAG_HEAD;
+
+	switch (pkt->pkt_cdbp[0]) {
+	case SCMD_DOORLOCK:
+		return (b2s_scmd_doorlock(ri));
+
+	case SCMD_FORMAT:
+		return (b2s_scmd_format(ri));
+
+	case SCMD_INQUIRY:
+		return (b2s_scmd_inq(ri));
+
+	case SCMD_REQUEST_SENSE:
+		return (b2s_scmd_rqs(ri));
+
+	case SCMD_SDIAG:
+		return (b2s_scmd_sdiag(ri));
+
+	case SCMD_TEST_UNIT_READY:
+		return (b2s_scmd_tur(ri));
+
+	case SCMD_READ_CAPACITY:
+		return (b2s_scmd_readcap(ri));
+
+	case SCMD_RELEASE:
+	case SCMD_RESERVE:
+		return (b2s_scmd_reserve_release(ri));
+
+	case SCMD_START_STOP:
+		return (b2s_scmd_start_stop(ri));
+
+	case SCMD_MODE_SENSE:
+		return (b2s_scmd_mode_sense(ri));
+
+	case SCMD_READ:
+	case SCMD_READ_G1:
+	case SCMD_WRITE:
+	case SCMD_WRITE_G1:
+		return (b2s_scmd_rw(ri));
+
+	default:
+		b2s_request_done(&ri->ri_public, B2S_ENOTSUP, 0);
+		return (TRAN_ACCEPT);
+	}
+}
+
+/*
+ * Publish standard properties on a newly created devinfo node.
+ */
+int
+b2s_update_props(dev_info_t *dip, b2s_leaf_t *l, char **compat, int ncompat)
+{
+	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, "target", l->l_target) !=
+	    DDI_PROP_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, "lun", l->l_lun) !=
+	    DDI_PROP_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pm-capable", 1) !=
+	    DDI_PROP_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, "compatible",
+	    compat, ncompat) != DDI_PROP_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+	if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, "unique-id",
+	    l->l_uuid) != DDI_PROP_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+
+	if (l->l_flags & B2S_LEAF_HOTPLUGGABLE) {
+		if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
+		    "hotpluggable") != DDI_PROP_SUCCESS) {
+			return (DDI_FAILURE);
+		}
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Find the devinfo node associated with the leaf, looking up by target and
+ * lun.  (Alternatively in the future we could use a full address)
+ *
+ * This must be called with the tree lock held.
+ */
+dev_info_t *
+b2s_find_node(b2s_nexus_t *n, b2s_leaf_t *l)
+{
+	dev_info_t	*dip;
+	int		tgt, lun;
+
+	dip = ddi_get_child(n->n_dip);
+	while (dip != NULL) {
+
+		tgt = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+		    DDI_PROP_DONTPASS, "target", -1);
+
+		lun = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+		    DDI_PROP_DONTPASS, "lun", -1);
+
+		/* is this the right target */
+		if ((lun == l->l_lun) && (tgt == l->l_target)) {
+			return (dip);
+		}
+
+		dip = ddi_get_next_sibling(dip);
+	}
+
+	return (NULL);
+
+}
+
+/*
+ * Create and attach a devinfo node for the supplied nexus/leaf
+ * combination.
+ */
+int
+b2s_create_node(b2s_nexus_t *n, b2s_leaf_t *l, dev_info_t **dipp)
+{
+	dev_info_t	*dip;
+	char		*name;
+	char		**compat;
+	int		ncompat;
+	int		rv;
+
+	/*
+	 * If the node was already created, then we're done.
+	 */
+	if ((dip = b2s_find_node(n, l)) != NULL) {
+		if (dipp)
+			*dipp = dip;
+		return (DDI_SUCCESS);
+	}
+
+	ASSERT(l != NULL);
+
+	/*
+	 * Perform an inquiry to collect key information.
+	 */
+	if (b2s_inquiry(l) != DDI_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+
+	scsi_hba_nodename_compatible_get(&l->l_inq, NULL, l->l_inq.inq_dtype,
+	    NULL, &name, &compat, &ncompat);
+
+	if (ndi_devi_alloc(n->n_dip, name, DEVI_SID_NODEID, &dip) !=
+	    NDI_SUCCESS) {
+		scsi_hba_nodename_compatible_free(name, compat);
+		b2s_warn(l, "Unable to create devinfo node");
+		return (DDI_FAILURE);
+	}
+
+	if (b2s_update_props(dip, l, compat, ncompat) != DDI_SUCCESS) {
+		scsi_hba_nodename_compatible_free(name, compat);
+		ndi_prop_remove_all(dip);
+		(void) ndi_devi_free(dip);
+		b2s_warn(l, "Unable to create properties");
+		return (DDI_FAILURE);
+	}
+	scsi_hba_nodename_compatible_free(name, compat);
+
+	if (dipp) {
+		/*
+		 * We were called by bus_config BUS_CONFIG_ONE,
+		 * and therefore must be done synchronously.
+		 */
+		rv = ndi_devi_online(dip, NDI_ONLINE_ATTACH);
+		if (rv == NDI_SUCCESS)
+			*dipp = dip;
+	} else {
+		/*
+		 * The rest of the time, asynchronous is easier and
+		 * safer (nexus could call us from interrupt context).
+		 */
+		rv = ndi_devi_online_async(dip,
+		    NDI_ONLINE_ATTACH | NDI_NOSLEEP);
+	}
+	if (rv != NDI_SUCCESS) {
+		b2s_warn(l, "Failed to online device");
+		ndi_prop_remove_all(dip);
+		(void) ndi_devi_free(dip);
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+int
+b2s_bus_config(dev_info_t *ndip, uint_t flag, ddi_bus_config_op_t op,
+    void *arg, dev_info_t **ldip)
+{
+	long			val;
+	char			*ptr;
+	int			rv;
+	scsi_hba_tran_t		*tran;
+	b2s_leaf_t		*l;
+	b2s_nexus_t		*n;
+	int			circ;
+	uint_t			target, lun;
+
+	tran = ddi_get_driver_private(ndip);
+	n = tran->tran_hba_private;
+
+	ndi_devi_enter(ndip, &circ);
+
+	switch (op) {
+	case BUS_CONFIG_ONE:
+
+		/*
+		 * First parse out the target and lun from the
+		 * address.
+		 */
+		if ((ptr = strchr((char *)arg, '@')) == NULL) {
+			rv = NDI_FAILURE;
+			break;
+		}
+		ptr++;
+		if ((ddi_strtol(ptr, &ptr, 16, &val) != 0) ||
+		    (val < 0) || (*ptr != ',')) {
+			rv = NDI_FAILURE;
+			break;
+		}
+		ptr++;
+		target = (uint_t)val;
+		if ((ddi_strtol(ptr, &ptr, 16, &val) != 0) ||
+		    (val < 0) || (*ptr != 0)) {
+			rv = NDI_FAILURE;
+			break;
+		}
+		lun = (uint_t)val;
+
+		/*
+		 * Now lookup the leaf, and if we have it, attempt to create
+		 * the devinfo node for it.
+		 */
+		rv = NDI_SUCCESS;
+		if ((l = b2s_hold_leaf(n, target, lun)) != NULL) {
+			if (b2s_create_node(n, l, ldip) != DDI_SUCCESS) {
+				rv = NDI_FAILURE;
+			}
+			b2s_rele_leaf(l);
+			break;
+		}
+		break;
+
+	case BUS_CONFIG_DRIVER:
+	case BUS_CONFIG_ALL:
+
+		l = b2s_next_leaf(n, NULL);
+		while (l != NULL) {
+			(void) b2s_create_node(n, l, NULL);
+			l = b2s_next_leaf(n, l);
+		}
+
+		rv = NDI_SUCCESS;
+		break;
+
+	default:
+		rv = NDI_FAILURE;
+		break;
+	}
+
+	if (rv == NDI_SUCCESS) {
+		rv = ndi_busop_bus_config(ndip, flag, op, arg, ldip, 0);
+	}
+
+	ndi_devi_exit(ndip, circ);
+	return (rv);
+}
+
+void
+b2s_inquiry_done(b2s_request_impl_t *ri)
+{
+	struct scsi_inquiry	*inqp = &ri->ri_leaf->l_inq;
+
+	/*
+	 * The only post processing we have to do is to massage the
+	 * strings into the inquiry structure.
+	 */
+	COPYSTR(ri->ri_inquiry.inq_vendor, inqp->inq_vid);
+	COPYSTR(ri->ri_inquiry.inq_product, inqp->inq_pid);
+	COPYSTR(ri->ri_inquiry.inq_revision, inqp->inq_revision);
+	COPYSTR(ri->ri_inquiry.inq_serial, inqp->inq_serial);
+}
+
+int
+b2s_inquiry(b2s_leaf_t *l)
+{
+	b2s_nexus_t		*n;
+	b2s_request_impl_t	*ri;
+	struct scsi_inquiry	*inqp;
+	int			err;
+
+	inqp = &l->l_inq;
+	n = l->l_nexus;
+
+	/*
+	 * Set up basic structure, including space padding for ASCII strings.
+	 */
+	bzero(inqp, sizeof (*inqp));
+	(void) memset(inqp->inq_vid, ' ', sizeof (inqp->inq_vid));
+	(void) memset(inqp->inq_pid, ' ', sizeof (inqp->inq_pid));
+	(void) memset(inqp->inq_revision, ' ', sizeof (inqp->inq_revision));
+	(void) memset(inqp->inq_serial, ' ', sizeof (inqp->inq_serial));
+	inqp->inq_len = sizeof (*inqp) - 4;
+	inqp->inq_ansi = 2;
+	inqp->inq_rdf = RDF_SCSI2;
+	inqp->inq_dtype = DTYPE_DIRECT;
+	if (l->l_flags & B2S_LEAF_REMOVABLE)
+		inqp->inq_rmb = 1;
+
+	/*
+	 * To get product strings, we have to issue a query to the driver.
+	 */
+	ri = kmem_zalloc(sizeof (*ri), KM_NOSLEEP);
+	if (ri == NULL) {
+		return (DDI_FAILURE);
+	}
+	ri->ri_cmd = B2S_CMD_INQUIRY;
+	ri->ri_target = l->l_target;
+	ri->ri_lun = l->l_lun;
+	ri->ri_flags = B2S_REQUEST_FLAG_HEAD;
+	ri->ri_leaf = l;
+	ri->ri_nexus = n;
+	ri->ri_done = b2s_inquiry_done;
+	/* leave all else null */
+
+	/*
+	 * Submit inquiry request to device driver.
+	 */
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		/* this shouldn't happen, since we are just starting out */
+		b2s_warn(l, "Busy trying to collect inquiry data");
+		kmem_free(ri, sizeof (*ri));
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Wait for inquiry completion.
+	 */
+	mutex_enter(&n->n_lock);
+	while ((ri->ri_flags & B2S_REQUEST_FLAG_DONE) == 0)
+		cv_wait(&n->n_cv, &n->n_lock);
+	mutex_exit(&n->n_lock);
+
+	err = ri->ri_errno;
+	kmem_free(ri, sizeof (*ri));
+
+	if (err != 0) {
+		b2s_warn(l, "Failed during inquiry (error %d)", err);
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+int
+b2s_scmd_inq(b2s_request_impl_t *ri)
+{
+	b2s_leaf_t		*l = ri->ri_leaf;
+	union scsi_cdb		*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+	caddr_t			ptr;
+	size_t			resid, len;
+	uint8_t			hdr[4];
+	const uint8_t		*data;
+	/*
+	 * Suppport inquiry pages: 0 is the list itself, and 80 is the
+	 * unit serial number (in ASCII).
+	 */
+	const uint8_t		supp[2] = { 0, 0x80 };
+
+	b2s_request_mapin(&ri->ri_public, &ptr, &len);
+
+	hdr[2] = 0;
+
+	/*
+	 * We don't support the EVP data bit, and hence neither a page code.
+	 * This corresponds to the entire G0 address field (which includes
+	 * a few reserved bits).
+	 */
+	switch (GETG0ADDR(cdb)) {
+	case 0x00000:		/* standard SCSI inquiry */
+		resid = min(sizeof (l->l_inq), GETG0COUNT(cdb));
+		len = min(resid, len);
+		bcopy(&l->l_inq, ptr, len);
+		ri->ri_resid = resid - len;
+		bcopy(&l->l_inq, ptr, len);
+		ri->ri_resid = resid - len;
+		b2s_request_done(&ri->ri_public, B2S_EOK, 0);
+		return (TRAN_ACCEPT);
+
+	case 0x10000:		/* page 0 supported VPD pages */
+		data = supp;
+		hdr[0] = DTYPE_DIRECT;
+		hdr[1] = 0;	/* page code */
+		hdr[2] = 0;
+		hdr[3] = 2;	/* page length */
+		break;
+
+	case 0x18000:		/* page 80 unit serial number */
+		data = (uint8_t *)l->l_uuid;
+		hdr[0] = DTYPE_DIRECT;
+		hdr[1] = 0x80;	/* page code */
+		hdr[2] = 0;
+		hdr[3] = l->l_uuid ? strlen(l->l_uuid) : 0; 	/* page len */
+		break;
+
+	default:
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	resid = min(hdr[3] + 4, GETG0COUNT(cdb));
+	len = min(resid, len);
+	ri->ri_resid = resid - len;
+
+	/* now copy the header */
+	len = min(resid, 4);
+	bcopy(hdr, ptr, len);
+	resid -= len;
+
+	/* now copy the actual page data */
+	bcopy(data, ptr + len, resid);
+
+	b2s_request_done(&ri->ri_public, B2S_EOK, 0);
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_rqs(b2s_request_impl_t *ri)
+{
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+	size_t		len, resid;
+	caddr_t		ptr;
+	int		rv;
+
+	/* Like inquiry, the entire G0 address field must be zero. */
+	if (GETG0ADDR(cdb) != 0) {
+		rv = B2S_EINVAL;
+		len = 0;
+		resid = 0;
+	} else {
+		struct scsi_extended_sense es;
+
+		/*
+		 * We always use ARQ, unconditionally, so this command
+		 * can always return success.
+		 */
+		bzero(&es, sizeof (es));
+		es.es_valid = 1;
+		es.es_class = CLASS_EXTENDED_SENSE;
+		es.es_key = KEY_NO_SENSE;
+
+		resid = sizeof (es);
+
+		b2s_request_mapin(&ri->ri_public, &ptr, &len);
+
+		len = min(resid, len);
+		bcopy(&es, ptr, len);
+		resid -= len;
+
+		rv = B2S_EOK;
+	}
+	b2s_request_done(&ri->ri_public, rv, resid);
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_sdiag(b2s_request_impl_t *ri)
+{
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+	int		rv;
+
+	/* we only support the SELFTEST bit */
+	if ((GETG0TAG(cdb) & 0x4) == 0) {
+		rv = B2S_EINVAL;
+	} else {
+		rv = B2S_EOK;
+	}
+	b2s_request_done(&ri->ri_public, rv, 0);
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_tur(b2s_request_impl_t *ri)
+{
+	b2s_nexus_t 	*n = ri->ri_nexus;
+
+	ri->ri_cmd = B2S_CMD_GETMEDIA;
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		return (TRAN_BUSY);
+	}
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_doorlock(b2s_request_impl_t *ri)
+{
+	b2s_nexus_t	*n = ri->ri_nexus;
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+
+	/*
+	 * Bit 0 of the count indicates the "Prevent" mode.  All other address
+	 * and count bits are reserved.
+	 */
+	if ((GETG0ADDR(cdb) != 0) || ((GETG0COUNT(cdb) & 0xFE) != 0)) {
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	ri->ri_cmd = (GETG0COUNT(cdb) != 0) ? B2S_CMD_LOCK : B2S_CMD_UNLOCK;
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		return (TRAN_BUSY);
+	}
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_format(b2s_request_impl_t *ri)
+{
+	b2s_nexus_t	*n = ri->ri_nexus;
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+	size_t		len;
+	caddr_t		ptr;
+
+	if (GETG0TAG(cdb) & 0x7) {
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	if (GETG0TAG(cdb) & FPB_DATA) {
+		/*
+		 * FmtData set.  A defect list is attached.
+		 *
+		 * This is an awful lot of work just to support a command
+		 * option we don't ever care about.  SCSI-2 says we have
+		 * to do it.
+		 *
+		 * The alternative would just be to ignore the defect list
+		 * and format options altogether.  That would be a lot easier.
+		 */
+
+		b2s_request_mapin(&ri->ri_public, &ptr, &len);
+
+		if (len < 4) {
+			b2s_request_done(&ri->ri_public, B2S_EBADMSG, 0);
+			return (TRAN_ACCEPT);
+		}
+
+		if ((ptr[0] != 0) || (ptr[2] != 0) || (ptr[3] != 0) ||
+		    ((ptr[1] & 0xF9) != 0)) {
+			b2s_request_done(&ri->ri_public, B2S_EPARAM, 0);
+			return (TRAN_ACCEPT);
+		}
+
+		if (ptr[1] & 0x2) {
+			ri->ri_flags |= B2S_REQUEST_FLAG_IMMED;
+		}
+
+	} else if (GETG0TAG(cdb) & FPB_CMPLT) {
+		/*
+		 * No defect list, so this bit (CmpLst) should have been zero!
+		 */
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	ri->ri_cmd = B2S_CMD_FORMAT;
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		return (TRAN_BUSY);
+	}
+
+	return (TRAN_ACCEPT);
+}
+
+void
+b2s_scmd_readcap_done(b2s_request_impl_t *ri)
+{
+	uint32_t		lba;
+	union scsi_cdb		*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+	struct scsi_capacity	cap;
+	caddr_t			ptr;
+	size_t			resid, len;
+
+	/*
+	 * Lower layer resid is meaningless here.
+	 */
+	if (ri->ri_errno != B2S_EOK) {
+		return;
+	}
+
+	lba = GETG1ADDR(cdb);
+
+	switch (GETG1COUNT(cdb)) {
+	case 0:	/* PMI == 0 */
+		if (lba != 0) {
+			ri->ri_errno = B2S_EINVAL;
+			return;
+		}
+		break;
+	case 1:	/* PMI == 1 */
+		if (lba >= ri->ri_media.media_nblks) {
+			ri->ri_errno = B2S_EBLKADDR;
+			return;
+		}
+		break;
+	default:
+		ri->ri_errno = B2S_EINVAL;
+		return;
+	}
+
+	/*
+	 * Note that the capacity is the LBA of the last block, not the
+	 * number of blocks.  A little surprising if you don't pay close
+	 * enough attention to the spec.
+	 */
+	SCSI_WRITE32(&cap.capacity, ri->ri_media.media_nblks - 1);
+	SCSI_WRITE32(&cap.lbasize, ri->ri_media.media_blksz);
+
+	b2s_request_mapin(&ri->ri_public, &ptr, &len);
+
+	if (len != 0) {
+		resid = sizeof (cap);
+		len = min(resid, len);
+		bcopy(&cap, ptr, len);
+		ri->ri_resid = resid - len;
+	}
+}
+
+int
+b2s_scmd_readcap(b2s_request_impl_t *ri)
+{
+	b2s_nexus_t	*n = ri->ri_nexus;
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+
+	/*
+	 * No transfer by real target.
+	 */
+	ri->ri_done = b2s_scmd_readcap_done;
+
+	if ((GETG1TAG(cdb)) != 0) {
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	ri->ri_cmd = B2S_CMD_GETMEDIA;
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		return (TRAN_BUSY);
+	}
+
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_reserve_release(b2s_request_impl_t *ri)
+{
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+
+	/* we aren't checking fields we don't care about */
+	if ((GETG0TAG(cdb) & 0x1) != 0)  {
+		/* extent reservations not supported */
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	/*
+	 * We don't support multi-initiator access, so we always
+	 * return success.
+	 */
+
+	b2s_request_done(&ri->ri_public, B2S_EOK, 0);
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_start_stop(b2s_request_impl_t *ri)
+{
+	b2s_nexus_t	*n = ri->ri_nexus;
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+	uint8_t		count;
+
+	switch (GETG0ADDR(cdb)) {
+	case 0:
+		break;
+	case 0x10000:	/* immed set */
+		ri->ri_flags |= B2S_REQUEST_FLAG_IMMED;
+		break;
+	default:
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+	count = GETG0COUNT(cdb);
+	if (count > 3) {
+		b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+		return (TRAN_ACCEPT);
+	}
+	if (count & 0x2)
+		ri->ri_flags |= B2S_REQUEST_FLAG_LOAD_EJECT;
+	if (count & 0x1) {
+		ri->ri_cmd = B2S_CMD_START;
+	} else {
+		ri->ri_cmd = B2S_CMD_STOP;
+	}
+
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		return (TRAN_BUSY);
+	}
+	return (TRAN_ACCEPT);
+}
+
+void
+b2s_scmd_mode_sense_done(b2s_request_impl_t *ri)
+{
+	uchar_t			*cdb = ri->ri_pkt->pkt_cdbp;
+	uint8_t			pc, page, devspec;
+	caddr_t			ptr;
+	size_t			len, resid;
+	uint8_t			data[16];
+
+	if ((ri->ri_errno == 0) &&
+	    ((ri->ri_media.media_flags & B2S_MEDIA_FLAG_READ_ONLY) == 0))  {
+		devspec = 0;
+	} else {
+		/* this marks the media read-only */
+		devspec = 0x80;
+	}
+
+	pc = page = cdb[2];
+	pc &= 0xc0;
+	page &= 0x3f;
+
+	/* we do not support savable parameters, at all */
+	if ((pc & 0xc0) == 0x3) {
+		ri->ri_errno = B2S_ENOSAV;
+		ri->ri_resid = 0;
+		return;
+	}
+
+	b2s_request_mapin(&ri->ri_public, &ptr, &resid);
+
+	if ((page == 0x9) || (page == 0x3f)) {
+		/* Peripheral device page */
+
+		/* header */
+		data[0] = 9 + 3;	/* length following */
+		data[1] = 0;		/* medium type */
+		data[2] = devspec;	/* mostly r/w flag */
+		data[3] = 0;		/* block descriptor len */
+		len = min(4, resid);
+
+		bcopy(data, ptr, len);
+		resid -= len;
+		ptr += len;
+
+		/* page data - 9 bytes long */
+		bzero(data, 9);
+		data[0] = 0x9;		/* page code */
+		data[1] = 0x8;		/* following data */
+		len = min(resid, 9);
+		bcopy(data, ptr, len);
+		resid -= len;
+		ptr += len;
+	}
+
+	if ((page == 0xa) || (page == 0x3f)) {
+		/* Control mode page */
+
+		/* header */
+		data[0] = 8 + 3;	/* length following */
+		data[1] = 0;		/* medium type */
+		data[2] = devspec;	/* mostly r/w flag */
+		data[3] = 0;		/* block descriptor len */
+		len = min(4, resid);
+
+		bcopy(data, ptr, len);
+		resid -= len;
+		ptr += len;
+
+		/* page data - 9 bytes long */
+		bzero(data, 8);
+		data[0] = 0xa;		/* page code */
+		data[1] = 0x7;		/* following data */
+		len = min(resid, 9);
+		bcopy(data, ptr, len);
+		resid -= len;
+		ptr += len;
+	}
+	ri->ri_resid = 0;
+	ri->ri_errno = B2S_EOK;
+}
+
+int
+b2s_scmd_mode_sense(b2s_request_impl_t *ri)
+{
+	b2s_nexus_t	*n = ri->ri_nexus;
+
+	ri->ri_done = b2s_scmd_mode_sense_done;
+	ri->ri_cmd = B2S_CMD_GETMEDIA;
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		return (TRAN_BUSY);
+	}
+	return (TRAN_ACCEPT);
+}
+
+int
+b2s_scmd_rw(b2s_request_impl_t *ri)
+{
+	b2s_nexus_t	*n = ri->ri_nexus;
+	uint32_t	lba;
+	uint32_t	nblks;
+	union scsi_cdb	*cdb = (void *)ri->ri_pkt->pkt_cdbp;
+
+	switch (GETGROUP(cdb)) {
+	case CDB_GROUPID_0:
+		nblks = GETG0COUNT(cdb);
+		nblks = nblks ? nblks : 256;
+		lba = GETG0ADDR(cdb);
+		break;
+	case CDB_GROUPID_1:
+		if (GETG1TAG(cdb)) {
+			/* we don't support relative addresses */
+			b2s_request_done(&ri->ri_public, B2S_EINVAL, 0);
+			return (TRAN_ACCEPT);
+		}
+		lba = GETG1ADDR(cdb);
+		nblks = GETG1COUNT(cdb);
+		break;
+	default:
+		b2s_request_done(&ri->ri_public, B2S_ENOTSUP, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	if (nblks == 0) {
+		b2s_request_done(&ri->ri_public, 0, 0);
+		return (TRAN_ACCEPT);
+	}
+
+	ri->ri_nblks = nblks;
+	ri->ri_lba = lba;
+	ri->ri_flags |= B2S_REQUEST_FLAG_BLKS;
+	ri->ri_cmd = (GETCMD(cdb)) == SCMD_READ ?
+	    B2S_CMD_READ : B2S_CMD_WRITE;
+
+	if (!n->n_request(n->n_private, &ri->ri_public)) {
+		return (TRAN_BUSY);
+	}
+	return (TRAN_ACCEPT);
+}
+
+void
+b2s_warn(b2s_leaf_t *l, const char *fmt, ...)
+{
+	va_list		ap;
+	b2s_nexus_t	*n;
+	char		msg[256];
+
+	n = l->l_nexus;
+
+	(void) snprintf(msg, sizeof (msg), "%s%d target %d lun %d: %s",
+	    ddi_driver_name(n->n_dip), ddi_get_instance(n->n_dip),
+	    l->l_target, l->l_lun, fmt);
+
+	va_start(ap, fmt);
+	vcmn_err(CE_WARN, msg, ap);
+	va_end(ap);
+}
+
+b2s_nexus_t *
+b2s_alloc_nexus(b2s_nexus_info_t *info)
+{
+	b2s_nexus_t		*n;
+	struct scsi_hba_tran	*tran;
+
+	if (info->nexus_version != B2S_VERSION_0)
+		return (NULL);
+
+	n = kmem_zalloc(sizeof (*n), KM_SLEEP);
+	mutex_init(&n->n_lock, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&n->n_cv, NULL, CV_DRIVER, NULL);
+	list_create(&n->n_leaves, sizeof (struct b2s_leaf),
+	    offsetof(struct b2s_leaf, l_node));
+
+	n->n_dip = info->nexus_dip;
+	n->n_private = info->nexus_private;
+	n->n_request = info->nexus_request;
+	if (info->nexus_dma_attr != NULL) {
+		n->n_dma = info->nexus_dma_attr;
+	} else {
+		n->n_dma = &b2s_default_dma_attr;
+	}
+
+	tran = scsi_hba_tran_alloc(n->n_dip, SCSI_HBA_CANSLEEP);
+	if (tran == NULL) {
+		list_destroy(&n->n_leaves);
+		mutex_destroy(&n->n_lock);
+		cv_destroy(&n->n_cv);
+		kmem_free(n, sizeof (*n));
+		return (NULL);
+	}
+	n->n_tran =  tran;
+
+	tran->tran_hba_dip =		n->n_dip;
+	tran->tran_hba_private =	n;
+	tran->tran_tgt_private =	NULL;
+	tran->tran_tgt_init =		b2s_tran_tgt_init;
+	tran->tran_tgt_free =		b2s_tran_tgt_free;
+	tran->tran_tgt_probe =		scsi_hba_probe;
+	tran->tran_tgt_free =		NULL;
+	tran->tran_start =		b2s_tran_start;
+	tran->tran_reset = 		b2s_tran_reset;
+	tran->tran_abort = 		b2s_tran_abort;
+	tran->tran_getcap = 		b2s_tran_getcap;
+	tran->tran_setcap = 		b2s_tran_setcap;
+	tran->tran_setup_pkt =		b2s_tran_setup_pkt;
+	tran->tran_teardown_pkt =	b2s_tran_teardown_pkt;
+	tran->tran_hba_len =		sizeof (b2s_request_impl_t);
+	tran->tran_bus_config =		b2s_bus_config;
+
+	return (n);
+}
+
+void
+b2s_free_nexus(b2s_nexus_t *n)
+{
+	b2s_leaf_t *l;
+
+	/*
+	 * Toss any registered leaves, if we haven't already done so.
+	 * At this point we don't care about upper layers, because the
+	 * DDI should not have allowed us to detach if there were busy
+	 * targets.
+	 */
+	while ((l = list_head(&n->n_leaves)) != NULL) {
+		list_remove(&n->n_leaves, l);
+		kmem_free(l, sizeof (struct b2s_leaf));
+	}
+	list_destroy(&n->n_leaves);
+	mutex_destroy(&n->n_lock);
+	cv_destroy(&n->n_cv);
+	kmem_free(n, sizeof (struct b2s_nexus));
+}
+
+int
+b2s_attach_nexus(b2s_nexus_t *n)
+{
+	int	rv;
+
+	rv = scsi_hba_attach_setup(n->n_dip, n->n_dma, n->n_tran,
+	    SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_TRAN_CLONE);
+	if (rv == 0) {
+		n->n_attached = B_TRUE;
+	}
+	return (rv);
+}
+
+int
+b2s_detach_nexus(b2s_nexus_t *n)
+{
+	int	rv;
+
+	if (n->n_attached) {
+		rv = scsi_hba_detach(n->n_dip);
+		if (rv == 0) {
+			n->n_attached = B_FALSE;
+		}
+	} else {
+		rv = 0;
+	}
+	return ((rv == 0) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+b2s_leaf_t *
+b2s_attach_leaf(b2s_nexus_t *n, b2s_leaf_info_t *info)
+{
+	b2s_leaf_t	*l;
+	uint_t		target	= info->leaf_target;
+	uint_t		lun	= info->leaf_lun;
+	const char	*uuid	= info->leaf_unique_id;
+	uint32_t	flags	= info->leaf_flags;
+
+	if (uuid == NULL) {
+		uuid = "";
+	}
+
+	mutex_enter(&n->n_lock);
+
+	/*
+	 * If the leaf already exists, it is a sign that the device
+	 * was kept around because it was still in use.  In that case,
+	 * we attempt to detect the situation where the node is the same
+	 * as the previous one, and reconnect it.
+	 */
+	if ((l = b2s_get_leaf(n, target, lun)) != NULL) {
+		if (strcmp(l->l_uuid, uuid) != 0) {
+			/*
+			 * Leaf already exists, but is not the same!  This
+			 * would be a good time to issue a warning.
+			 */
+			mutex_exit(&n->n_lock);
+			b2s_warn(l, "Target disconnected while still in use.");
+			b2s_warn(l, "Reconnect the previous target device.");
+			return (NULL);
+		}
+		l->l_flags &= ~B2S_LEAF_DETACHED;
+	} else {
+		if ((l = kmem_zalloc(sizeof (*l), KM_NOSLEEP)) == NULL) {
+			mutex_exit(&n->n_lock);
+			b2s_warn(l, "Unable to allocate target state.");
+			return (NULL);
+		}
+		l->l_nexus = n;
+		l->l_target = target;
+		l->l_lun = lun;
+		l->l_flags = flags;
+
+		/* strdup would be nice here */
+		l->l_uuid = kmem_alloc(strlen(uuid) + 1, KM_NOSLEEP);
+		if (l->l_uuid == NULL) {
+			mutex_exit(&n->n_lock);
+			kmem_free(l, sizeof (*l));
+			b2s_warn(l, "Unable to allocate target UUID storage.");
+			return (NULL);
+		}
+		(void) strcpy(l->l_uuid, uuid);
+
+		list_insert_tail(&n->n_leaves, l);
+	}
+
+	/*
+	 * Make sure we hold it, so that it won't be freed out from
+	 * underneath us.
+	 */
+	l->l_refcnt++;
+	mutex_exit(&n->n_lock);
+
+	/*
+	 * If the HBA is currently attached, then we need to attach
+	 * the node right now.  This supports "hotplug".  Note that
+	 * if the node is a reinsert, then this should degenerate into
+	 * a NOP.
+	 */
+	if (n->n_attached) {
+		int	circ;
+		ndi_devi_enter(n->n_dip, &circ);
+		(void) b2s_create_node(n, l, NULL);
+		ndi_devi_exit(n->n_dip, circ);
+	}
+
+	return (l);
+}
+
+void
+b2s_detach_leaf(b2s_leaf_t *l)
+{
+	b2s_nexus_t	*n = l->l_nexus;
+	dev_info_t	*dip;
+	int		circ;
+
+	l->l_flags |= B2S_LEAF_DETACHED;
+
+	/*
+	 * Search for an appropriate child devinfo.
+	 */
+	ndi_devi_enter(n->n_dip, &circ);
+	dip = b2s_find_node(n, l);
+	if (dip != NULL) {
+		(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
+	}
+	ndi_devi_exit(n->n_dip, circ);
+
+	b2s_rele_leaf(l);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,1467 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include "sdhost.h"
+
+typedef	struct sdslot	sdslot_t;
+typedef	struct sdhost	sdhost_t;
+
+/*
+ * Per slot state.
+ */
+struct sdslot {
+	sda_host_t		*ss_host;
+	int			ss_num;
+	ddi_acc_handle_t	ss_acch;
+	caddr_t 		ss_regva;
+	kmutex_t		ss_lock;
+	uint32_t		ss_capab;
+	uint32_t		ss_baseclk;	/* Hz */
+	uint32_t		ss_cardclk;	/* Hz */
+	uint8_t			ss_tmoutclk;
+	uint32_t		ss_tmusecs;	/* timeout units in usecs */
+	uint32_t		ss_ocr;		/* OCR formatted voltages */
+	uint16_t		ss_mode;
+	boolean_t		ss_suspended;
+
+	/*
+	 * Command in progress
+	 */
+	uint8_t			*ss_kvaddr;
+	ddi_dma_cookie_t	*ss_dmacs;
+	uint_t			ss_ndmac;
+	int			ss_blksz;
+	uint16_t		ss_resid;	/* in blocks */
+
+	/* scratch buffer, to receive extra PIO data */
+	uint32_t		ss_bounce[2048 / 4];
+};
+
+/*
+ * Per controller state.
+ */
+struct sdhost {
+	int			sh_numslots;
+	ddi_dma_attr_t		sh_dmaattr;
+	sdslot_t		sh_slots[SDHOST_MAXSLOTS];
+	sda_host_t		*sh_host;
+
+	/*
+	 * Interrupt related information.
+	 */
+	ddi_intr_handle_t	sh_ihandle;
+	int			sh_icap;
+	uint_t			sh_ipri;
+};
+
+
+static int sdhost_attach(dev_info_t *, ddi_attach_cmd_t);
+static int sdhost_detach(dev_info_t *, ddi_detach_cmd_t);
+static int sdhost_suspend(dev_info_t *);
+static int sdhost_resume(dev_info_t *);
+
+static void sdhost_enable_interrupts(sdslot_t *);
+static void sdhost_disable_interrupts(sdslot_t *);
+static int sdhost_setup_intr(dev_info_t *, sdhost_t *);
+static uint_t sdhost_intr(caddr_t, caddr_t);
+static int sdhost_init_slot(dev_info_t *, sdhost_t *, int, int);
+static void sdhost_uninit_slot(sdhost_t *, int);
+static sda_err_t sdhost_soft_reset(sdslot_t *, uint8_t);
+static sda_err_t sdhost_set_clock(sdslot_t *, uint32_t);
+static void sdhost_xfer_done(sdslot_t *, sda_err_t);
+static sda_err_t sdhost_wait_cmd(sdslot_t *, sda_cmd_t *);
+static uint_t sdhost_slot_intr(sdslot_t *);
+
+static sda_err_t sdhost_cmd(void *, sda_cmd_t *);
+static sda_err_t sdhost_getprop(void *, sda_prop_t, uint32_t *);
+static sda_err_t sdhost_setprop(void *, sda_prop_t, uint32_t);
+static sda_err_t sdhost_poll(void *);
+static sda_err_t sdhost_reset(void *);
+static sda_err_t sdhost_halt(void *);
+
+static struct dev_ops sdhost_dev_ops = {
+	DEVO_REV,			/* devo_rev */
+	0,				/* devo_refcnt */
+	ddi_no_info,			/* devo_getinfo */
+	nulldev,			/* devo_identify */
+	nulldev,			/* devo_probe */
+	sdhost_attach,			/* devo_attach */
+	sdhost_detach,			/* devo_detach */
+	nodev,				/* devo_reset */
+	NULL,				/* devo_cb_ops */
+	NULL,				/* devo_bus_ops */
+	NULL				/* devo_power */
+};
+
+static struct modldrv sdhost_modldrv = {
+	&mod_driverops,			/* drv_modops */
+	"Standard SD Host Controller",	/* drv_linkinfo */
+	&sdhost_dev_ops			/* drv_dev_ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,			/* ml_rev */
+	{ &sdhost_modldrv, NULL }	/* ml_linkage */
+};
+
+static struct sda_ops sdhost_ops = {
+	SDA_OPS_VERSION,
+	sdhost_cmd,			/* so_cmd */
+	sdhost_getprop,			/* so_getprop */
+	sdhost_setprop,			/* so_setprop */
+	sdhost_poll,			/* so_poll */
+	sdhost_reset,			/* so_reset */
+	sdhost_halt,			/* so_halt */
+};
+
+static ddi_device_acc_attr_t sdhost_regattr = {
+	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
+	DDI_STRUCTURE_LE_ACC,	/* devacc_attr_endian_flags */
+	DDI_STRICTORDER_ACC,	/* devacc_attr_dataorder */
+	DDI_DEFAULT_ACC,	/* devacc_attr_access */
+};
+
+#define	GET16(ss, reg)	\
+	ddi_get16(ss->ss_acch, (void *)(ss->ss_regva + reg))
+#define	PUT16(ss, reg, val)	\
+	ddi_put16(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
+#define	GET32(ss, reg)	\
+	ddi_get32(ss->ss_acch, (void *)(ss->ss_regva + reg))
+#define	PUT32(ss, reg, val)	\
+	ddi_put32(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
+#define	GET64(ss, reg)	\
+	ddi_get64(ss->ss_acch, (void *)(ss->ss_regva + reg))
+
+#define	GET8(ss, reg)	\
+	ddi_get8(ss->ss_acch, (void *)(ss->ss_regva + reg))
+#define	PUT8(ss, reg, val)	\
+	ddi_put8(ss->ss_acch, (void *)(ss->ss_regva + reg), val)
+
+#define	CLR8(ss, reg, mask)	PUT8(ss, reg, GET8(ss, reg) & ~(mask))
+#define	SET8(ss, reg, mask)	PUT8(ss, reg, GET8(ss, reg) | (mask))
+
+/*
+ * If ever anyone uses PIO on SPARC, we have to endian-swap.  But we
+ * think that SD Host Controllers are likely to be uncommon on SPARC,
+ * and hopefully when they exist at all they will be able to use DMA.
+ */
+#ifdef	_BIG_ENDIAN
+#define	sw32(x)		ddi_swap32(x)
+#define	sw16(x)		ddi_swap16(x)
+#else
+#define	sw32(x)		(x)
+#define	sw16(x)		(x)
+#endif
+
+#define	GETDATA32(ss)		sw32(GET32(ss, REG_DATA))
+#define	GETDATA16(ss)		sw16(GET16(ss, REG_DATA))
+#define	GETDATA8(ss)		GET8(ss, REG_DATA)
+
+#define	PUTDATA32(ss, val)	PUT32(ss, REG_DATA, sw32(val))
+#define	PUTDATA16(ss, val)	PUT16(ss, REG_DATA, sw16(val))
+#define	PUTDATA8(ss, val)	PUT8(ss, REG_DATA, val)
+
+#define	CHECK_STATE(ss, nm)	\
+	((GET32(ss, REG_PRS) & PRS_ ## nm) != 0)
+
+int
+_init(void)
+{
+	int	rv;
+
+	sda_host_init_ops(&sdhost_dev_ops);
+
+	if ((rv = mod_install(&modlinkage)) != 0) {
+		sda_host_fini_ops(&sdhost_dev_ops);
+	}
+
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int	rv;
+
+	if ((rv = mod_remove(&modlinkage)) == 0) {
+		sda_host_fini_ops(&sdhost_dev_ops);
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+sdhost_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	sdhost_t		*shp;
+	ddi_acc_handle_t	pcih;
+	uint8_t			slotinfo;
+	uint8_t			bar;
+	int			i;
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		break;
+
+	case DDI_RESUME:
+		return (sdhost_resume(dip));
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Soft state allocation.
+	 */
+	shp = kmem_zalloc(sizeof (*shp), KM_SLEEP);
+	ddi_set_driver_private(dip, shp);
+
+	/*
+	 * Initialize DMA attributes.  For now we initialize as for
+	 * SDMA.  If we add ADMA support we can improve this.
+	 */
+	shp->sh_dmaattr.dma_attr_version = DMA_ATTR_V0;
+	shp->sh_dmaattr.dma_attr_addr_lo = 0;
+	shp->sh_dmaattr.dma_attr_addr_hi = 0xffffffffU;
+	shp->sh_dmaattr.dma_attr_count_max = 0xffffffffU;
+	shp->sh_dmaattr.dma_attr_align = 1;
+	shp->sh_dmaattr.dma_attr_burstsizes = 0;	/* for now! */
+	shp->sh_dmaattr.dma_attr_minxfer = 1;
+	shp->sh_dmaattr.dma_attr_maxxfer = 0xffffffffU;
+	shp->sh_dmaattr.dma_attr_sgllen = -1;		/* unlimited! */
+	shp->sh_dmaattr.dma_attr_seg = 0xfff;		/* 4K segments */
+	shp->sh_dmaattr.dma_attr_granular = 1;
+	shp->sh_dmaattr.dma_attr_flags = 0;
+
+	/*
+	 * PCI configuration access to figure out number of slots present.
+	 */
+	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "pci_config_setup failed");
+		goto failed;
+	}
+
+	slotinfo = pci_config_get8(pcih, SLOTINFO);
+	shp->sh_numslots = SLOTINFO_NSLOT(slotinfo);
+
+	if (shp->sh_numslots > SDHOST_MAXSLOTS) {
+		cmn_err(CE_WARN, "Host reports to have too many slots: %d",
+		    shp->sh_numslots);
+		goto failed;
+	}
+
+	/*
+	 * Enable master accesses and DMA.
+	 */
+	pci_config_put16(pcih, PCI_CONF_COMM,
+	    pci_config_get16(pcih, PCI_CONF_COMM) |
+	    PCI_COMM_MAE | PCI_COMM_ME);
+
+	/*
+	 * Figure out which BAR to use.  Note that we number BARs from
+	 * 1, although PCI and SD Host numbers from 0.  (We number
+	 * from 1, because register number 0 means PCI configuration
+	 * space in Solaris.)
+	 */
+	bar = SLOTINFO_BAR(slotinfo) + 1;
+
+	pci_config_teardown(&pcih);
+
+	/*
+	 * Setup interrupts ... supports the new DDI interrupt API.  This
+	 * will support MSI or MSI-X interrupts if a device is found to
+	 * support it.
+	 */
+	if (sdhost_setup_intr(dip, shp) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "Failed to setup interrupts");
+		goto failed;
+	}
+
+	shp->sh_host = sda_host_alloc(dip, shp->sh_numslots, &sdhost_ops,
+	    &shp->sh_dmaattr);
+	if (shp->sh_host == NULL) {
+		cmn_err(CE_WARN, "Failed allocating SD host structure");
+		goto failed;
+	}
+
+	/*
+	 * Configure slots, this also maps registers, enables
+	 * interrupts, etc.  Most of the hardware setup is done here.
+	 */
+	for (i = 0; i < shp->sh_numslots; i++) {
+		if (sdhost_init_slot(dip, shp, i, bar + i) != DDI_SUCCESS) {
+			cmn_err(CE_WARN, "Failed initializing slot %d", i);
+			goto failed;
+		}
+	}
+
+	ddi_report_dev(dip);
+
+	/*
+	 * Enable device interrupts at the DDI layer.
+	 */
+	(void) ddi_intr_enable(shp->sh_ihandle);
+
+	/*
+	 * Mark the slots online with the framework.  This will cause
+	 * the framework to probe them for the presence of cards.
+	 */
+	if (sda_host_attach(shp->sh_host) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "Failed attaching to SDA framework");
+		(void) ddi_intr_disable(shp->sh_ihandle);
+		goto failed;
+	}
+
+	return (DDI_SUCCESS);
+
+failed:
+	if (shp->sh_ihandle != NULL) {
+		(void) ddi_intr_remove_handler(shp->sh_ihandle);
+		(void) ddi_intr_free(shp->sh_ihandle);
+	}
+	for (i = 0; i < shp->sh_numslots; i++)
+		sdhost_uninit_slot(shp, i);
+	kmem_free(shp, sizeof (*shp));
+
+	return (DDI_FAILURE);
+}
+
+int
+sdhost_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	sdhost_t	*shp;
+	int		i;
+
+	switch (cmd) {
+	case DDI_DETACH:
+		break;
+
+	case DDI_SUSPEND:
+		return (sdhost_suspend(dip));
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	shp = ddi_get_driver_private(dip);
+	if (shp == NULL)
+		return (DDI_FAILURE);
+
+	/*
+	 * Take host offline with the framework.
+	 */
+	sda_host_detach(shp->sh_host);
+
+	/*
+	 * Tear down interrupts.
+	 */
+	if (shp->sh_ihandle != NULL) {
+		(void) ddi_intr_disable(shp->sh_ihandle);
+		(void) ddi_intr_remove_handler(shp->sh_ihandle);
+		(void) ddi_intr_free(shp->sh_ihandle);
+	}
+
+	/*
+	 * Tear down register mappings, etc.
+	 */
+	for (i = 0; i < shp->sh_numslots; i++)
+		sdhost_uninit_slot(shp, i);
+	kmem_free(shp, sizeof (*shp));
+
+	return (DDI_SUCCESS);
+}
+
+int
+sdhost_suspend(dev_info_t *dip)
+{
+	sdhost_t	*shp;
+	sdslot_t	*ss;
+	int		i;
+
+	shp = ddi_get_driver_private(dip);
+	if (shp == NULL)
+		return (DDI_FAILURE);
+
+	/* disable the interrupts */
+	(void) ddi_intr_disable(shp->sh_ihandle);
+
+	for (i = 0; i < shp->sh_numslots; i++) {
+		ss = &shp->sh_slots[i];
+		mutex_enter(&ss->ss_lock);
+		ss->ss_suspended = B_TRUE;
+		sdhost_disable_interrupts(ss);
+		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
+		mutex_exit(&ss->ss_lock);
+	}
+	return (DDI_SUCCESS);
+}
+
+int
+sdhost_resume(dev_info_t *dip)
+{
+	sdhost_t	*shp;
+	sdslot_t	*ss;
+	int		i;
+
+	shp = ddi_get_driver_private(dip);
+	if (shp == NULL)
+		return (DDI_FAILURE);
+
+	for (i = 0; i < shp->sh_numslots; i++) {
+		ss = &shp->sh_slots[i];
+		mutex_enter(&ss->ss_lock);
+		ss->ss_suspended = B_FALSE;
+		(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
+		sdhost_enable_interrupts(ss);
+		mutex_exit(&ss->ss_lock);
+	}
+
+	/* re-enable the interrupts */
+	(void) ddi_intr_enable(shp->sh_ihandle);
+
+	/* kick off a new card detect task */
+	for (i = 0; i < shp->sh_numslots; i++) {
+		ss = &shp->sh_slots[i];
+		sda_host_detect(ss->ss_host, ss->ss_num);
+	}
+	return (DDI_SUCCESS);
+}
+
+sda_err_t
+sdhost_set_clock(sdslot_t *ss, uint32_t hz)
+{
+	uint16_t	div;
+	uint32_t	val;
+	uint32_t	clk;
+	int		count;
+
+	/*
+	 * Shut off the clock to begin.
+	 */
+	ss->ss_cardclk = 0;
+	PUT16(ss, REG_CLOCK_CONTROL, 0);
+	if (hz == 0) {
+		return (SDA_EOK);
+	}
+
+	if (ss->ss_baseclk == 0) {
+		sda_host_log(ss->ss_host, ss->ss_num,
+		    "Base clock frequency not established.");
+		return (SDA_EINVAL);
+	}
+
+	if ((hz > 25000000) && ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0)) {
+		/* this clock requires high speed timings! */
+		SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
+	} else {
+		/* don't allow clock to run faster than 25MHz */
+		hz = min(hz, 25000000);
+		CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
+	}
+
+	/* figure out the divider */
+	clk = ss->ss_baseclk;
+	div  = 1;
+	while (clk > hz) {
+		if (div > 0x80)
+			break;
+		clk >>= 1;	/* divide clock by two */
+		div <<= 1;	/* divider goes up by one */
+	}
+	div >>= 1;	/* 0 == divide by 1, 1 = divide by 2 */
+
+	/*
+	 * Set the internal clock divider first, without enabling the
+	 * card clock yet.
+	 */
+	PUT16(ss, REG_CLOCK_CONTROL,
+	    (div << CLOCK_CONTROL_FREQ_SHIFT) | CLOCK_CONTROL_INT_CLOCK_EN);
+
+	/*
+	 * Wait up to 100 msec for the internal clock to stabilize.
+	 * (The spec does not seem to indicate a maximum timeout, but
+	 * it also suggests that an infinite loop be used, which is
+	 * not appropriate for hardened Solaris drivers.)
+	 */
+	for (count = 100000; count; count -= 10) {
+
+		val = GET16(ss, REG_CLOCK_CONTROL);
+
+		if (val & CLOCK_CONTROL_INT_CLOCK_STABLE) {
+			/* if clock is stable, enable the SD clock pin */
+			PUT16(ss, REG_CLOCK_CONTROL, val |
+			    CLOCK_CONTROL_SD_CLOCK_EN);
+
+			ss->ss_cardclk = clk;
+			return (SDA_EOK);
+		}
+
+		drv_usecwait(10);
+	}
+
+	return (SDA_ETIME);
+}
+
+sda_err_t
+sdhost_soft_reset(sdslot_t *ss, uint8_t bits)
+{
+	int	count;
+
+	/*
+	 * There appears to be a bug where Ricoh hosts might have a
+	 * problem if the host frequency is not set.  If the card
+	 * isn't present, or we are doing a master reset, just enable
+	 * the internal clock at its native speed.  (No dividers, and
+	 * not exposed to card.).
+	 */
+	if ((bits == SOFT_RESET_ALL) || !(CHECK_STATE(ss, CARD_INSERTED))) {
+		PUT16(ss, REG_CLOCK_CONTROL, CLOCK_CONTROL_INT_CLOCK_EN);
+		/* simple 1msec wait, don't wait for clock to stabilize */
+		drv_usecwait(1000);
+	}
+
+	PUT8(ss, REG_SOFT_RESET, bits);
+	for (count = 100000; count != 0; count -= 10) {
+		if ((GET8(ss, REG_SOFT_RESET) & bits) == 0) {
+			return (SDA_EOK);
+		}
+		drv_usecwait(10);
+	}
+
+	return (SDA_ETIME);
+}
+
+void
+sdhost_disable_interrupts(sdslot_t *ss)
+{
+	/* disable slot interrupts for card insert and remove */
+	PUT16(ss, REG_INT_MASK, 0);
+	PUT16(ss, REG_INT_EN, 0);
+
+	/* disable error interrupts */
+	PUT16(ss, REG_ERR_MASK, 0);
+	PUT16(ss, REG_ERR_EN, 0);
+}
+
+void
+sdhost_enable_interrupts(sdslot_t *ss)
+{
+	/*
+	 * Note that we want to enable reading of the CMD related
+	 * bits, but we do not want them to generate an interrupt.
+	 * (The busy wait for typical CMD stuff will normally be less
+	 * than 10usec, so its simpler/easier to just poll.  Even in
+	 * the worst case of 100 kHz, the poll is at worst 2 msec.)
+	 */
+
+	/* enable slot interrupts for card insert and remove */
+	PUT16(ss, REG_INT_MASK, INT_MASK);
+	PUT16(ss, REG_INT_EN, INT_ENAB);
+
+	/* enable error interrupts */
+	PUT16(ss, REG_ERR_MASK, ERR_MASK);
+	PUT16(ss, REG_ERR_EN, ERR_ENAB);
+}
+
+int
+sdhost_setup_intr(dev_info_t *dip, sdhost_t *shp)
+{
+	int		itypes;
+	int		itype;
+
+	/*
+	 * Set up interrupt handler.
+	 */
+	if (ddi_intr_get_supported_types(dip, &itypes) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "ddi_intr_get_supported_types failed");
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Interrupt types are bits in a mask.  We know about these ones:
+	 * FIXED = 1
+	 * MSI = 2
+	 * MSIX = 4
+	 */
+	for (itype = DDI_INTR_TYPE_MSIX; itype != 0; itype >>= 1) {
+
+		int			count;
+
+		if ((itypes & itype) == 0) {
+			/* this type is not supported on this device! */
+			continue;
+		}
+
+		if ((ddi_intr_get_nintrs(dip, itype, &count) != DDI_SUCCESS) ||
+		    (count == 0)) {
+			cmn_err(CE_WARN, "ddi_intr_get_nintrs failed");
+			continue;
+		}
+
+		/*
+		 * We have not seen a host device with multiple
+		 * interrupts (one per slot?), and the spec does not
+		 * indicate that they exist.  But if one ever occurs,
+		 * we spew a warning to help future debugging/support
+		 * efforts.
+		 */
+		if (count > 1) {
+			cmn_err(CE_WARN, "Controller offers %d interrupts, "
+			    "but driver only supports one", count);
+			continue;
+		}
+
+		if ((ddi_intr_alloc(dip, &shp->sh_ihandle, itype, 0, 1,
+		    &count, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
+		    (count != 1)) {
+			cmn_err(CE_WARN, "ddi_intr_alloc failed");
+			continue;
+		}
+
+		if (ddi_intr_get_pri(shp->sh_ihandle, &shp->sh_ipri) !=
+		    DDI_SUCCESS) {
+			cmn_err(CE_WARN, "ddi_intr_get_pri failed");
+			(void) ddi_intr_free(shp->sh_ihandle);
+			shp->sh_ihandle = NULL;
+			continue;
+		}
+
+		if (shp->sh_ipri >= ddi_intr_get_hilevel_pri()) {
+			cmn_err(CE_WARN, "Hi level interrupt not supported");
+			(void) ddi_intr_free(shp->sh_ihandle);
+			shp->sh_ihandle = NULL;
+			continue;
+		}
+
+		if (ddi_intr_get_cap(shp->sh_ihandle, &shp->sh_icap) !=
+		    DDI_SUCCESS) {
+			cmn_err(CE_WARN, "ddi_intr_get_cap failed");
+			(void) ddi_intr_free(shp->sh_ihandle);
+			shp->sh_ihandle = NULL;
+			continue;
+		}
+
+		if (ddi_intr_add_handler(shp->sh_ihandle, sdhost_intr,
+		    shp, NULL) != DDI_SUCCESS) {
+			cmn_err(CE_WARN, "ddi_intr_add_handler failed");
+			(void) ddi_intr_free(shp->sh_ihandle);
+			shp->sh_ihandle = NULL;
+			continue;
+		}
+
+		return (DDI_SUCCESS);
+	}
+
+	return (DDI_FAILURE);
+}
+
+void
+sdhost_xfer_done(sdslot_t *ss, sda_err_t errno)
+{
+	if ((errno == SDA_EOK) && (ss->ss_resid != 0)) {
+		/* an unexpected partial transfer was found */
+		errno = SDA_ERESID;
+	}
+	ss->ss_blksz = 0;
+	ss->ss_resid = 0;
+
+	if (errno != SDA_EOK) {
+		(void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
+		(void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
+
+		/* send a STOP command if necessary */
+		if (ss->ss_mode & XFR_MODE_AUTO_CMD12) {
+			PUT32(ss, REG_ARGUMENT, 0);
+			PUT16(ss, REG_COMMAND,
+			    (CMD_STOP_TRANSMIT << 8) |
+			    COMMAND_TYPE_NORM | COMMAND_INDEX_CHECK_EN |
+			    COMMAND_CRC_CHECK_EN | COMMAND_RESP_48_BUSY);
+		}
+	}
+
+	sda_host_transfer(ss->ss_host, ss->ss_num, errno);
+}
+
+uint_t
+sdhost_slot_intr(sdslot_t *ss)
+{
+	uint16_t	intr;
+	uint16_t	errs;
+	uint8_t		*data;
+	int		count;
+
+	mutex_enter(&ss->ss_lock);
+
+	if (ss->ss_suspended) {
+		mutex_exit(&ss->ss_lock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	intr = GET16(ss, REG_INT_STAT);
+	if (intr == 0) {
+		mutex_exit(&ss->ss_lock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+	errs = GET16(ss, REG_ERR_STAT);
+
+	if (intr & (INT_REM | INT_INS)) {
+
+		PUT16(ss, REG_INT_STAT, intr);
+		mutex_exit(&ss->ss_lock);
+
+		sda_host_detect(ss->ss_host, ss->ss_num);
+		/* no further interrupt processing this cycle */
+		return (DDI_INTR_CLAIMED);
+	}
+
+	if (intr & INT_DMA) {
+		/*
+		 * We have crossed a DMA/page boundary.  Cope with it.
+		 */
+		if (ss->ss_ndmac) {
+			ss->ss_ndmac--;
+			ss->ss_dmacs++;
+			PUT16(ss, REG_INT_STAT, INT_DMA);
+			PUT32(ss, REG_SDMA_ADDR, ss->ss_dmacs->dmac_address);
+
+		} else {
+			/*
+			 * Apparently some sdhost controllers issue a
+			 * final DMA interrupt if the DMA completes on
+			 * a boundary, even though there is no further
+			 * data to transfer.
+			 *
+			 * There might be a risk here of the
+			 * controller continuing to access the same
+			 * data over and over again, but we accept the
+			 * risk.
+			 */
+			PUT16(ss, REG_INT_STAT, INT_DMA);
+		}
+	}
+
+	if (intr & INT_RD) {
+		/*
+		 * PIO read!  PIO is quite suboptimal, but we expect
+		 * performance critical applications to use DMA
+		 * whenever possible.  We have to stage this through
+		 * the bounce buffer to meet alignment considerations.
+		 */
+
+		PUT16(ss, REG_INT_STAT, INT_RD);
+
+		while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_RD_EN)) {
+
+			data = (void *)ss->ss_bounce;
+			count = ss->ss_blksz;
+
+			ASSERT(count > 0);
+			ASSERT(ss->ss_kvaddr != NULL);
+
+			while (count >= sizeof (uint32_t)) {
+				*(uint32_t *)(void *)data = GETDATA32(ss);
+				data += sizeof (uint32_t);
+				count -= sizeof (uint32_t);
+			}
+			while (count >= sizeof (uint16_t)) {
+				*(uint16_t *)(void *)data = GETDATA16(ss);
+				data += sizeof (uint16_t);
+				count -= sizeof (uint16_t);
+			}
+			while (count >= sizeof (uint8_t)) {
+				*(uint8_t *)data = GETDATA8(ss);
+				data += sizeof (uint8_t);
+				count -= sizeof (uint8_t);
+			}
+
+			bcopy(ss->ss_bounce, ss->ss_kvaddr, ss->ss_blksz);
+			ss->ss_kvaddr += ss->ss_blksz;
+			ss->ss_resid--;
+		}
+	}
+
+	if (intr & INT_WR) {
+		/*
+		 * PIO write!  PIO is quite suboptimal, but we expect
+		 * performance critical applications to use DMA
+		 * whenever possible.  We have to stage this trhough
+		 * the bounce buffer to meet alignment considerations.
+		 */
+
+		PUT16(ss, REG_INT_STAT, INT_WR);
+
+		while ((ss->ss_resid > 0) && CHECK_STATE(ss, BUF_WR_EN)) {
+
+			data = (void *)ss->ss_bounce;
+			count = ss->ss_blksz;
+
+			ASSERT(count > 0);
+			ASSERT(ss->ss_kvaddr != NULL);
+
+			bcopy(ss->ss_kvaddr, data, count);
+			while (count >= sizeof (uint32_t)) {
+				PUTDATA32(ss, *(uint32_t *)(void *)data);
+				data += sizeof (uint32_t);
+				count -= sizeof (uint32_t);
+			}
+			while (count >= sizeof (uint16_t)) {
+				PUTDATA16(ss, *(uint16_t *)(void *)data);
+				data += sizeof (uint16_t);
+				count -= sizeof (uint16_t);
+			}
+			while (count >= sizeof (uint8_t)) {
+				PUTDATA8(ss, *(uint8_t *)data);
+				data += sizeof (uint8_t);
+				count -= sizeof (uint8_t);
+			}
+
+			ss->ss_kvaddr += ss->ss_blksz;
+			ss->ss_resid--;
+		}
+	}
+
+	if (intr & INT_XFR) {
+		PUT16(ss, REG_INT_STAT, INT_XFR);
+
+		sdhost_xfer_done(ss, SDA_EOK);
+	}
+
+	if (intr & INT_ERR) {
+		PUT16(ss, REG_ERR_STAT, errs);
+		PUT16(ss, REG_INT_STAT, INT_ERR);
+
+		if (errs & ERR_DAT) {
+			if ((errs & ERR_DAT_END) == ERR_DAT_END) {
+				sdhost_xfer_done(ss, SDA_EPROTO);
+			} else if ((errs & ERR_DAT_CRC) == ERR_DAT_CRC) {
+				sdhost_xfer_done(ss, SDA_ECRC7);
+			} else {
+				sdhost_xfer_done(ss, SDA_ETIME);
+			}
+
+		} else if (errs & ERR_ACMD12) {
+			/*
+			 * Generally, this is bad news.  we need a full
+			 * reset to recover properly.
+			 */
+			sdhost_xfer_done(ss, SDA_ECMD12);
+		}
+
+		/*
+		 * This asynchronous error leaves the slot more or less
+		 * useless.  Report it to the framework.
+		 */
+		if (errs & ERR_CURRENT) {
+			sda_host_fault(ss->ss_host, ss->ss_num,
+			    SDA_FAULT_CURRENT);
+		}
+	}
+
+	mutex_exit(&ss->ss_lock);
+
+	return (DDI_INTR_CLAIMED);
+}
+
+/*ARGSUSED1*/
+uint_t
+sdhost_intr(caddr_t arg1, caddr_t arg2)
+{
+	sdhost_t	*shp = (void *)arg1;
+	int		rv = DDI_INTR_UNCLAIMED;
+	int		num;
+
+	/* interrupt for each of the slots present in the system */
+	for (num = 0; num < shp->sh_numslots; num++) {
+		if (sdhost_slot_intr(&shp->sh_slots[num]) ==
+		    DDI_INTR_CLAIMED) {
+			rv = DDI_INTR_CLAIMED;
+		}
+	}
+	return (rv);
+}
+
+int
+sdhost_init_slot(dev_info_t *dip, sdhost_t *shp, int num, int bar)
+{
+	sdslot_t	*ss;
+	uint32_t	capab;
+	uint32_t	clk;
+
+	/*
+	 * Register the private state.
+	 */
+	ss = &shp->sh_slots[num];
+	ss->ss_host = shp->sh_host;
+	ss->ss_num = num;
+	sda_host_set_private(shp->sh_host, num, ss);
+
+	/*
+	 * Initialize core data structure, locks, etc.
+	 */
+	mutex_init(&ss->ss_lock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(shp->sh_ipri));
+
+	if (ddi_regs_map_setup(dip, bar, &ss->ss_regva, 0, 0, &sdhost_regattr,
+	    &ss->ss_acch) != DDI_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+
+	/* reset before reading capabilities */
+	if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK)
+		return (DDI_FAILURE);
+
+	capab = GET64(ss, REG_CAPAB) & 0xffffffffU; /* upper bits reserved */
+	ss->ss_capab = capab;
+
+	/* host voltages in OCR format */
+	ss->ss_ocr = 0;
+	if (capab & CAPAB_18V)
+		ss->ss_ocr |= OCR_18_19V;	/* 1.8V */
+	if (capab & CAPAB_30V)
+		ss->ss_ocr |= OCR_30_31V;
+	if (capab & CAPAB_33V)
+		ss->ss_ocr |= OCR_32_33V;
+
+	/* base clock */
+	ss->ss_baseclk =
+	    ((capab & CAPAB_BASE_FREQ_MASK) >> CAPAB_BASE_FREQ_SHIFT);
+	ss->ss_baseclk *= 1000000;
+
+	/*
+	 * Timeout clock.  We can calculate this using the following
+	 * formula:
+	 *
+	 * (1000000 usec/1sec) * (1sec/tmoutclk) * base factor = clock time
+	 *
+	 * Clock time is the length of the base clock in usecs.
+	 *
+	 * Our base factor is 2^13, which is the shortest clock we
+	 * can count.
+	 *
+	 * To simplify the math and avoid overflow, we cancel out the
+	 * zeros for kHz or MHz.  Since we want to wait more clocks, not
+	 * less, on error, we truncate the result rather than rounding
+	 * up.
+	 */
+	clk = ((capab & CAPAB_TIMEOUT_FREQ_MASK) >> CAPAB_TIMEOUT_FREQ_SHIFT);
+	if ((ss->ss_baseclk == 0) || (clk == 0)) {
+		cmn_err(CE_WARN, "Unable to determine clock frequencies");
+		return (DDI_FAILURE);
+	}
+
+	if (capab & CAPAB_TIMEOUT_UNITS) {
+		/* MHz */
+		ss->ss_tmusecs = (1 << 13) / clk;
+		clk *= 1000000;
+	} else {
+		/* kHz */
+		ss->ss_tmusecs = (1000 * (1 << 13)) / clk;
+		clk *= 1000;
+	}
+
+	/*
+	 * Calculation of the timeout.
+	 *
+	 * SDIO cards use a 1sec timeout, and SDHC cards use fixed
+	 * 100msec for read and 250 msec for write.
+	 *
+	 * Legacy cards running at 375kHz have a worst case of about
+	 * 15 seconds.  Running at 25MHz (the standard speed) it is
+	 * about 100msec for read, and about 3.2 sec for write.
+	 * Typical values are 1/100th that, or about 1msec for read,
+	 * and 32 msec for write.
+	 *
+	 * No transaction at full speed should ever take more than 4
+	 * seconds.  (Some slow legacy cards might have trouble, but
+	 * we'll worry about them if they ever are seen.  Nobody wants
+	 * to wait 4 seconds to access a single block anyway!)
+	 *
+	 * To get to 4 seconds, we continuously double usec until we
+	 * get to the maximum value, or a timeout greater than 4
+	 * seconds.
+	 *
+	 * Note that for high-speed timeout clocks, we might not be
+	 * able to get to the full 4 seconds.  E.g. with a 48MHz
+	 * timeout clock, we can only get to about 2.8 seconds.  Its
+	 * possible that there could be some slow MMC cards that will
+	 * timeout at this clock rate, but it seems unlikely.  (The
+	 * device would have to be pressing the very worst times,
+	 * against the 100-fold "permissive" window allowed, and
+	 * running at only 12.5MHz.)
+	 *
+	 * XXX: this could easily be a tunable.  Someone dealing with only
+	 * reasonable cards could set this to just 1 second.
+	 */
+	for (ss->ss_tmoutclk = 0; ss->ss_tmoutclk < 14; ss->ss_tmoutclk++) {
+		if ((ss->ss_tmusecs * (1 << ss->ss_tmoutclk)) >= 4000000) {
+			break;
+		}
+	}
+
+	/*
+	 * Enable slot interrupts.
+	 */
+	sdhost_enable_interrupts(ss);
+
+	return (DDI_SUCCESS);
+}
+
+void
+sdhost_uninit_slot(sdhost_t *shp, int num)
+{
+	sdslot_t	*ss;
+
+	ss = &shp->sh_slots[num];
+	if (ss->ss_acch == NULL)
+		return;
+
+	(void) sdhost_soft_reset(ss, SOFT_RESET_ALL);
+
+	ddi_regs_map_free(&ss->ss_acch);
+	mutex_destroy(&ss->ss_lock);
+}
+
+void
+sdhost_get_response(sdslot_t *ss, sda_cmd_t *cmdp)
+{
+	uint32_t	*resp = cmdp->sc_response;
+	int		i;
+
+	resp[0] = GET32(ss, REG_RESP1);
+	resp[1] = GET32(ss, REG_RESP2);
+	resp[2] = GET32(ss, REG_RESP3);
+	resp[3] = GET32(ss, REG_RESP4);
+
+	/*
+	 * Response 2 is goofy because the host drops the low
+	 * order CRC bits.  This makes it a bit awkward, so we
+	 * have to shift the bits to make it work out right.
+	 *
+	 * Note that the framework expects the 32 bit
+	 * words to be ordered in LE fashion.  (The
+	 * bits within the words are in native order).
+	 */
+	if (cmdp->sc_rtype == R2) {
+		for (i = 3; i > 0; i--) {
+			resp[i] <<= 8;
+			resp[i] |= (resp[i - 1] >> 24);
+		}
+		resp[0] <<= 8;
+	}
+}
+
+sda_err_t
+sdhost_wait_cmd(sdslot_t *ss, sda_cmd_t *cmdp)
+{
+	int		i;
+	uint16_t	errs;
+
+	/*
+	 * Worst case for 100kHz timeout is 2msec (200 clocks), we add
+	 * a tiny bit for safety.  (Generally timeout will be far, far
+	 * less than that.)
+	 *
+	 * Note that at more typical 12MHz (and normally it will be
+	 * even faster than that!) that the device timeout is only
+	 * 16.67 usec.  We could be smarter and reduce the delay time,
+	 * but that would require putting more intelligence into the
+	 * code, and we don't expect CMD timeout to normally occur
+	 * except during initialization.  (At which time we need the
+	 * full timeout anyway.)
+	 *
+	 * Checking the ERR_STAT will normally cause the timeout to
+	 * terminate to finish early if the device is healthy, anyway.
+	 */
+
+	for (i = 3000; i > 0; i -= 5) {
+		if (GET16(ss, REG_INT_STAT) & INT_CMD) {
+
+			PUT16(ss, REG_INT_STAT, INT_CMD);
+
+			/* command completed */
+			sdhost_get_response(ss, cmdp);
+			return (SDA_EOK);
+		}
+
+		if ((errs = (GET16(ss, REG_ERR_STAT) & ERR_CMD)) != 0) {
+			PUT16(ss, REG_ERR_STAT, errs);
+
+			/* command timeout isn't a host failure */
+			if ((errs & ERR_CMD_TMO) == ERR_CMD_TMO) {
+				return (SDA_ETIME);
+			}
+
+			if ((errs & ERR_CMD_CRC) == ERR_CMD_CRC) {
+				return (SDA_ECRC7);
+			} else {
+				return (SDA_EPROTO);
+			}
+		}
+
+		drv_usecwait(5);
+	}
+
+	return (SDA_ETIME);
+}
+
+sda_err_t
+sdhost_poll(void *arg)
+{
+	sdslot_t	*ss = arg;
+
+	(void) sdhost_slot_intr(ss);
+	return (SDA_EOK);
+}
+
+sda_err_t
+sdhost_cmd(void *arg, sda_cmd_t *cmdp)
+{
+	sdslot_t	*ss = arg;
+	uint16_t	command;
+	uint16_t	mode;
+	sda_err_t	rv;
+
+	/*
+	 * Command register:
+	 * bit 13-8	= command index
+	 * bit 7-6	= command type (always zero for us!)
+	 * bit 5	= data present select
+	 * bit 4	= command index check (always on!)
+	 * bit 3	= command CRC check enable
+	 * bit 2	= reserved
+	 * bit 1-0	= response type
+	 */
+
+	command = ((uint16_t)cmdp->sc_index << 8);
+	command |= COMMAND_TYPE_NORM |
+	    COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN;
+
+	switch (cmdp->sc_rtype) {
+	case R0:
+		command |= COMMAND_RESP_NONE;
+		break;
+	case R1:
+	case R5:
+	case R6:
+	case R7:
+		command |= COMMAND_RESP_48;
+		break;
+	case R1b:
+	case R5b:
+		command |= COMMAND_RESP_48_BUSY;
+		break;
+	case R2:
+		command |= COMMAND_RESP_136;
+		command &= ~(COMMAND_INDEX_CHECK_EN | COMMAND_CRC_CHECK_EN);
+		break;
+	case R3:
+	case R4:
+		command |= COMMAND_RESP_48;
+		command &= ~COMMAND_CRC_CHECK_EN;
+		command &= ~COMMAND_INDEX_CHECK_EN;
+		break;
+	default:
+		return (SDA_EINVAL);
+	}
+
+	mutex_enter(&ss->ss_lock);
+	if (ss->ss_suspended) {
+		mutex_exit(&ss->ss_lock);
+		return (SDA_ESUSPENDED);
+	}
+
+	if (cmdp->sc_nblks != 0) {
+		uint16_t	blksz;
+		uint16_t	nblks;
+
+		blksz = cmdp->sc_blksz;
+		nblks = cmdp->sc_nblks;
+
+		/*
+		 * Ensure that we have good data.
+		 */
+		if ((blksz < 1) || (blksz > 2048)) {
+			mutex_exit(&ss->ss_lock);
+			return (SDA_EINVAL);
+		}
+		command |= COMMAND_DATA_PRESENT;
+
+		ss->ss_blksz = blksz;
+
+		/*
+		 * Only SDMA for now.  We can investigate ADMA2 later.
+		 * (Right now we don't have ADMA2 capable hardware.)
+		 */
+		if (((ss->ss_capab & CAPAB_SDMA) != 0) &&
+		    (cmdp->sc_ndmac != 0)) {
+			ddi_dma_cookie_t	*dmacs = cmdp->sc_dmacs;
+
+			ASSERT(dmacs != NULL);
+
+			ss->ss_kvaddr = NULL;
+			ss->ss_resid = 0;
+			ss->ss_dmacs = dmacs;
+			ss->ss_ndmac = cmdp->sc_ndmac - 1;
+
+			PUT32(ss, REG_SDMA_ADDR, dmacs->dmac_address);
+			mode = XFR_MODE_DMA_EN;
+			PUT16(ss, REG_BLKSZ, blksz);
+
+		} else {
+			ss->ss_kvaddr = (void *)cmdp->sc_kvaddr;
+			ss->ss_resid = nblks;
+			ss->ss_dmacs = NULL;
+			ss->ss_ndmac = 0;
+			mode = 0;
+			PUT16(ss, REG_BLKSZ, blksz);
+		}
+
+		if (nblks > 1) {
+			mode |= XFR_MODE_MULTI | XFR_MODE_COUNT;
+			if (cmdp->sc_flags & SDA_CMDF_AUTO_CMD12)
+				mode |= XFR_MODE_AUTO_CMD12;
+		}
+		if ((cmdp->sc_flags & SDA_CMDF_READ) != 0) {
+			mode |= XFR_MODE_READ;
+		}
+
+		ss->ss_mode = mode;
+
+		PUT8(ss, REG_TIMEOUT_CONTROL, ss->ss_tmoutclk);
+		PUT16(ss, REG_BLOCK_COUNT, nblks);
+		PUT16(ss, REG_XFR_MODE, mode);
+	}
+
+	PUT32(ss, REG_ARGUMENT, cmdp->sc_argument);
+	PUT16(ss, REG_COMMAND, command);
+
+	rv = sdhost_wait_cmd(ss, cmdp);
+
+	mutex_exit(&ss->ss_lock);
+
+	return (rv);
+}
+
+sda_err_t
+sdhost_getprop(void *arg, sda_prop_t prop, uint32_t *val)
+{
+	sdslot_t	*ss = arg;
+	sda_err_t	rv = 0;
+
+	mutex_enter(&ss->ss_lock);
+
+	if (ss->ss_suspended) {
+		mutex_exit(&ss->ss_lock);
+		return (SDA_ESUSPENDED);
+	}
+
+	switch (prop) {
+	case SDA_PROP_INSERTED:
+		if (CHECK_STATE(ss, CARD_INSERTED)) {
+			*val = B_TRUE;
+		} else {
+			*val = B_FALSE;
+		}
+		break;
+
+	case SDA_PROP_WPROTECT:
+		if (CHECK_STATE(ss, WRITE_ENABLE)) {
+			*val = B_FALSE;
+		} else {
+			*val = B_TRUE;
+		}
+		break;
+
+	case SDA_PROP_OCR:
+		*val = ss->ss_ocr;
+		break;
+
+	case SDA_PROP_CLOCK:
+		*val = ss->ss_cardclk;
+		break;
+
+	case SDA_PROP_CAP_HISPEED:
+		if ((ss->ss_capab & CAPAB_HIGH_SPEED) != 0) {
+			*val = B_TRUE;
+		} else {
+			*val = B_FALSE;
+		}
+		break;
+
+	case SDA_PROP_CAP_4BITS:
+		*val = B_TRUE;
+		break;
+
+	case SDA_PROP_CAP_NOPIO:
+		if ((ss->ss_capab & CAPAB_SDMA) != 0) {
+			*val = B_TRUE;
+		} else {
+			*val = B_FALSE;
+		}
+		break;
+
+	case SDA_PROP_CAP_INTR:
+	case SDA_PROP_CAP_8BITS:
+		*val = B_FALSE;
+		break;
+
+	default:
+		rv = SDA_ENOTSUP;
+		break;
+	}
+	mutex_exit(&ss->ss_lock);
+
+	return (rv);
+}
+
+sda_err_t
+sdhost_setprop(void *arg, sda_prop_t prop, uint32_t val)
+{
+	sdslot_t	*ss = arg;
+	sda_err_t	rv = SDA_EOK;
+
+	mutex_enter(&ss->ss_lock);
+
+	if (ss->ss_suspended) {
+		mutex_exit(&ss->ss_lock);
+		return (SDA_ESUSPENDED);
+	}
+
+	switch (prop) {
+	case SDA_PROP_LED:
+		if (val) {
+			SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
+		} else {
+			CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_LED_ON);
+		}
+		break;
+
+	case SDA_PROP_CLOCK:
+		rv = sdhost_set_clock(arg, val);
+		break;
+
+	case SDA_PROP_BUSWIDTH:
+		switch (val) {
+		case 1:
+			CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
+			break;
+		case 4:
+			SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_DATA_WIDTH);
+			break;
+		default:
+			rv = SDA_EINVAL;
+		}
+		break;
+
+	case SDA_PROP_OCR:
+		val &= ss->ss_ocr;
+
+		if (val & OCR_17_18V) {
+			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V);
+			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_18V |
+			    POWER_CONTROL_BUS_POWER);
+		} else if (val & OCR_29_30V) {
+			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V);
+			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_30V |
+			    POWER_CONTROL_BUS_POWER);
+		} else if (val & OCR_32_33V) {
+			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V);
+			PUT8(ss, REG_POWER_CONTROL, POWER_CONTROL_33V |
+			    POWER_CONTROL_BUS_POWER);
+		} else if (val == 0) {
+			/* turn off power */
+			PUT8(ss, REG_POWER_CONTROL, 0);
+		} else {
+			rv = SDA_EINVAL;
+		}
+		break;
+
+	case SDA_PROP_HISPEED:
+		if (val) {
+			SET8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
+		} else {
+			CLR8(ss, REG_HOST_CONTROL, HOST_CONTROL_HIGH_SPEED_EN);
+		}
+		/* give clocks time to settle */
+		drv_usecwait(10);
+		break;
+
+	default:
+		rv = SDA_ENOTSUP;
+		break;
+	}
+
+	/*
+	 * Apparently some controllers (ENE) have issues with changing
+	 * certain parameters (bus width seems to be one), requiring
+	 * a reset of the DAT and CMD lines.
+	 */
+	if (rv == SDA_EOK) {
+		(void) sdhost_soft_reset(ss, SOFT_RESET_CMD);
+		(void) sdhost_soft_reset(ss, SOFT_RESET_DAT);
+	}
+	mutex_exit(&ss->ss_lock);
+	return (rv);
+}
+
+sda_err_t
+sdhost_reset(void *arg)
+{
+	sdslot_t	*ss = arg;
+
+	mutex_enter(&ss->ss_lock);
+	if (!ss->ss_suspended) {
+		if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
+			mutex_exit(&ss->ss_lock);
+			return (SDA_ETIME);
+		}
+		sdhost_enable_interrupts(ss);
+	}
+	mutex_exit(&ss->ss_lock);
+	return (SDA_EOK);
+}
+
+sda_err_t
+sdhost_halt(void *arg)
+{
+	sdslot_t	*ss = arg;
+
+	mutex_enter(&ss->ss_lock);
+	if (!ss->ss_suspended) {
+		sdhost_disable_interrupts(ss);
+		/* this has the side effect of removing power from the card */
+		if (sdhost_soft_reset(ss, SOFT_RESET_ALL) != SDA_EOK) {
+			mutex_exit(&ss->ss_lock);
+			return (SDA_ETIME);
+		}
+	}
+	mutex_exit(&ss->ss_lock);
+	return (SDA_EOK);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.h	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,299 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SDCARD_SDHOST_H
+#define	_SYS_SDCARD_SDHOST_H
+
+/*
+ * The entire contents of this file are private the SD Host driver
+ * implementation.
+ */
+
+#include <sys/types.h>
+#include <sys/ksynch.h>
+#include <sys/param.h>
+#include <sys/kmem.h>
+#include <sys/inttypes.h>
+#include <sys/cmn_err.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sdcard/sda.h>
+#include <sys/pci.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#define	BIT(x)	(1 << (x))
+
+/*
+ * SD Host Spec says that a controller can support up to 6 different
+ * slots, each with its own register set.
+ */
+#define	SDHOST_MAXSLOTS	6
+
+/*
+ * SD Host specific PCI configuration register.
+ */
+#define	SLOTINFO		0x40
+#define	SLOTINFO_NSLOT_SHFT	4
+#define	SLOTINFO_NSLOT_MSK	(0x3 << SLOTINFO_NSLOT_SHFT)
+#define	SLOTINFO_BAR_SHFT	0
+#define	SLOTINFO_BAR_MSK	(0x3 << SLOTINFO_BAR_SHFT)
+
+#define	SLOTINFO_NSLOT(x)		\
+	((((x) & SLOTINFO_NSLOT_MSK) >> SLOTINFO_NSLOT_SHFT) + 1)
+
+#define	SLOTINFO_BAR(x)		\
+	(((x) & SLOTINFO_BAR_MSK) >> SLOTINFO_BAR_SHFT)
+
+/*
+ * Slot-specific CSRs
+ */
+#define	REG_SDMA_ADDR			0x0000	/* 32 bits */
+#define	REG_BLKSZ			0x0004	/* 16 bits */
+#define	REG_BLOCK_COUNT			0x0006	/* 16 bits */
+#define	REG_ARGUMENT			0x0008	/* 32 bits */
+#define	REG_XFR_MODE			0x000C	/* 16 bits */
+#define	REG_COMMAND			0x000E	/* 16 bits */
+#define	REG_RESP1			0x0010	/* 32 bits */
+#define	REG_RESP2			0x0014	/* 32 bits */
+#define	REG_RESP3			0x0018	/* 32 bits */
+#define	REG_RESP4			0x001C	/* 32 bits */
+#define	REG_DATA			0x0020	/* 32 bits */
+#define	REG_PRS				0x0024	/* 32 bits */
+#define	REG_HOST_CONTROL		0x0028	/* 8 bits */
+#define	REG_POWER_CONTROL		0x0029	/* 8 bits */
+#define	REG_BLOCK_GAP_CONTROL		0x002A	/* 8 bits */
+#define	REG_WAKEUP_CONTROL		0x002B	/* 8 bits */
+#define	REG_CLOCK_CONTROL		0x002C	/* 16 bits */
+#define	REG_TIMEOUT_CONTROL		0x002E	/* 8 bits */
+#define	REG_SOFT_RESET			0x002F	/* 8 bits */
+#define	REG_INT_STAT			0x0030	/* 16 bits */
+#define	REG_ERR_STAT			0x0032	/* 16 bits */
+#define	REG_INT_EN			0x0034	/* 16 bits */
+#define	REG_ERR_EN			0x0036	/* 16 bits */
+#define	REG_INT_MASK			0x0038	/* 16 bits */
+#define	REG_ERR_MASK			0x003A	/* 16 bits */
+#define	REG_ACMD12_ERROR		0x003C	/* 16 bits */
+#define	REG_CAPAB			0x0040	/* 64 bits */
+#define	REG_MAX_CURRENT			0x0048	/* 64 bits */
+#define	REG_SLOT_INT_STAT		0x00FC	/* 16 bits */
+#define	REG_VERSION			0x00FE	/* 16 bits */
+#define	REG_ERR_FORCE			0x0052	/* 16 bits */
+#define	REG_ACMD12_ERROR_FORCE		0x0050	/* 16 bits */
+#define	REG_ADMA_ERROR			0x0054	/* 8 bits */
+#define	REG_ADMA_ADDR			0x0058	/* 64 bits */
+
+/* REG_BLKSZ bits */
+#define	BLKSZ_XFR_BLK_SIZE_MASK		(0x0fff)
+#define	BLKSZ_BOUNDARY_4K		(0 << 12)
+#define	BLKSZ_BOUNDARY_8K		(1 << 12)
+#define	BLKSZ_BOUNDARY_16K		(2 << 12)
+#define	BLKSZ_BOUNDARY_32K		(3 << 12)
+#define	BLKSZ_BOUNDARY_64K		(4 << 12)
+#define	BLKSZ_BOUNDARY_128K		(5 << 12)
+#define	BLKSZ_BOUNDARY_256K		(6 << 12)
+#define	BLKSZ_BOUNDARY_512K		(7 << 12)
+#define	BLKSZ_BOUNDARY_MASK		(0x7 << 12)
+
+/* REG_XFR_MODE bits */
+#define	XFR_MODE_DMA_EN			BIT(0)
+#define	XFR_MODE_COUNT			BIT(1)
+#define	XFR_MODE_AUTO_CMD12		BIT(2)
+#define	XFR_MODE_READ			BIT(4)	/* 1 = read, 0 = write */
+#define	XFR_MODE_MULTI			BIT(5)	/* 1 = multi, 0 = single */
+
+/* REG_COMMAND bits */
+#define	COMMAND_CRC_CHECK_EN		BIT(3)
+#define	COMMAND_INDEX_CHECK_EN		BIT(4)
+#define	COMMAND_DATA_PRESENT		BIT(5)
+#define	COMMAND_TYPE
+#define	COMMAND_TYPE_NORM		(0 << 6)
+#define	COMMAND_TYPE_SUSPEND		(1 << 6)
+#define	COMMAND_TYPE_RESUME		(2 << 6)
+#define	COMMAND_TYPE_ABORT		(3 << 6)
+#define	COMMAND_TYPE_MASK		(0x3 << 6)
+#define	COMMAND_RESP_NONE		0
+#define	COMMAND_RESP_136		1	/* R2 */
+#define	COMMAND_RESP_48			2	/* R1, R3, R6, R7 */
+#define	COMMAND_RESP_48_BUSY		3	/* R1b */
+
+/* REG_PRS bits */
+#define	PRS_CMD_INHIBIT			BIT(0)
+#define	PRS_DAT_INHIBIT			BIT(1)
+#define	PRS_DAT_ACTIVE			BIT(2)
+#define	PRS_WRITE_ACTIVE		BIT(8)
+#define	PRS_READ_ACTIVE			BIT(9)
+#define	PRS_BUF_WR_EN			BIT(10)
+#define	PRS_BUF_RD_EN			BIT(11)
+#define	PRS_CARD_INSERTED		BIT(16)
+#define	PRS_CARD_STABLE			BIT(17)
+#define	PRS_CARD_DETECT			BIT(18)
+#define	PRS_WRITE_ENABLE		BIT(19)
+#define	PRS_DAT0_SIG			BIT(20)
+#define	PRS_DAT1_SIG			BIT(21)
+#define	PRS_DAT2_SIG			BIT(22)
+#define	PRS_DAT3_SIG			BIT(23)
+
+#define	PRS_INHIBIT    \
+	(PRS_CMD_INHIBIT | PRS_DAT_INHIBIT)
+#define	PRS_DAT_SIG	\
+	(PRS_DAT0_SIG | PRS_DAT1_SIG | PRS_DAT2_SIG | PRS_DAT3_SIG)
+
+/* REG_HOST_CONTROL bits */
+#define	HOST_CONTROL_LED_ON		BIT(0)
+#define	HOST_CONTROL_DATA_WIDTH		BIT(1)
+#define	HOST_CONTROL_HIGH_SPEED_EN	BIT(2)
+#define	HOST_CONTROL_DMA_SDMA		(0 << 3)
+#define	HOST_CONTROL_DMA_ADMA32		(2 << 3)
+#define	HOST_CONTROL_DMA_ADMA64		(3 << 3)
+#define	HOST_CONTROL_DMA_MASK		(0x3 << 3)
+#define	HOST_CONTROL_CARD_DETECT_TEST	BIT(6)
+#define	HOST_CONTROL_CARD_DETECT_SEL	BIT(7)
+
+/* REG_POWER_CONTROL bits */
+#define	POWER_CONTROL_BUS_POWER		BIT(0)
+#define	POWER_CONTROL_33V		(7 << 1)
+#define	POWER_CONTROL_30V		(6 << 1)
+#define	POWER_CONTROL_18V		(5 << 1)
+
+/* REG_BLOCK_GAP_CONTROL bits */
+#define	BLOCK_GAP_CONTROL_STOP		BIT(0)
+#define	BLOCK_GAP_CONTROL_CONTINUE	BIT(1)
+#define	BLOCK_GAP_CONTROL_READ_WAIT	BIT(2)
+#define	BLOCK_GAP_CONTROL_INTERRUPT	BIT(3)
+
+/* REG_WAKEUP_CONTROL bits */
+#define	WAKEUP_CONTROL_INTERRUPT	BIT(0)
+#define	WAKEUP_CONTROL_INSERT		BIT(1)
+#define	WAKEUP_CONTROL_REMOVE		BIT(2)
+
+/* REG_CLOCK_CONTROL bits */
+#define	CLOCK_CONTROL_INT_CLOCK_EN	BIT(0)
+#define	CLOCK_CONTROL_INT_CLOCK_STABLE	BIT(1)
+#define	CLOCK_CONTROL_SD_CLOCK_EN	BIT(2)
+#define	CLOCK_CONTROL_FREQ_MASK		(0xff << 8)
+#define	CLOCK_CONTROL_FREQ_SHIFT	8
+
+/* REG_TIMEOUT_CONTROL bits */
+#define	TIMEOUT_TIMECLK_2_27		(0xe)
+/* not listing them all here... but it goes on */
+#define	TIMEOUT_TIMECLK_2_13		(0x0)
+
+/* REG_SOFT_RESET bits */
+#define	SOFT_RESET_ALL			BIT(0)
+#define	SOFT_RESET_CMD			BIT(1)
+#define	SOFT_RESET_DAT			BIT(2)
+
+/* REG_INT_{STAT,EN,MASK} bits */
+#define	INT_CMD				BIT(0)
+#define	INT_XFR				BIT(1)
+#define	INT_BG				BIT(2)
+#define	INT_DMA				BIT(3)
+#define	INT_WR				BIT(4)
+#define	INT_RD				BIT(5)
+#define	INT_INS				BIT(6)
+#define	INT_REM				BIT(7)
+#define	INT_CARD			BIT(8)
+#define	INT_ERR				BIT(15)
+
+#define	INT_PIO		(INT_RD | INT_WR)
+#define	INT_HOTPLUG	(INT_INS | INT_REM)
+
+#define	INT_MASK	(INT_XFR | INT_DMA | INT_PIO | INT_HOTPLUG)
+#define	INT_ENAB	(INT_MASK | INT_CMD)
+
+/* REG_ERR_{STAT,EN,MASK} bits */
+#define	ERR_VENDOR			(0xf << 12)
+#define	ERR_ADMA			BIT(9)
+#define	ERR_ACMD12			BIT(8)
+#define	ERR_CURRENT			BIT(7)
+#define	ERR_DAT_END			BIT(6)
+#define	ERR_DAT_CRC			BIT(5)
+#define	ERR_DAT_TMO			BIT(4)
+#define	ERR_CMD_IDX			BIT(3)
+#define	ERR_CMD_END			BIT(2)
+#define	ERR_CMD_CRC			BIT(1)
+#define	ERR_CMD_TMO			BIT(0)
+
+#define	ERR_CMD		(ERR_CMD_IDX | ERR_CMD_END | ERR_CMD_CRC | ERR_CMD_TMO)
+#define	ERR_CMD_CFL	(ERR_CMD_CRC | ERR_CMD_TMO)
+
+#define	ERR_DAT		(ERR_DAT_END | ERR_DAT_CRC | ERR_DAT_TMO)
+
+#define	ERR_MASK	(ERR_ACMD12 | ERR_DAT)
+#define	ERR_ENAB	(ERR_MASK | ERR_CMD)
+
+/* REG_ACMD12_ERROR bits */
+#define	ACMD12_ERROR_NOT_EXECUTED	BIT(0)
+#define	ACMD12_ERROR_TIMEOUT		BIT(1)
+#define	ACMD12_ERROR_CRC		BIT(2)
+#define	ACMD12_ERROR_END_BIT		BIT(3)
+#define	ACMD12_ERROR_INDEX		BIT(4)
+#define	ACMD12_ERROR_NOT_ISSUED		BIT(7)
+
+/* REG_CAPAB bits */
+#define	CAPAB_TIMEOUT_FREQ_SHIFT	0
+#define	CAPAB_TIMEOUT_FREQ_MASK		(0x3f << 0)
+#define	CAPAB_TIMEOUT_UNITS		BIT(7)		/* 1 == MHz, 0 = kHz */
+#define	CAPAB_BASE_FREQ_SHIFT		8
+#define	CAPAB_BASE_FREQ_MASK		(0x3f << 8)
+#define	CAPAB_MAXBLK_512		(0 << 16)
+#define	CAPAB_MAXBLK_1K			(1 << 16)
+#define	CAPAB_MAXBLK_2K			(2 << 16)
+#define	CAPAB_MAXBLK_MASK		(0x3 << 16)
+#define	CAPAB_ADMA2			BIT(19)
+#define	CAPAB_ADMA1			BIT(20)
+#define	CAPAB_HIGH_SPEED		BIT(21)
+#define	CAPAB_SDMA			BIT(22)
+#define	CAPAB_SUSPEND			BIT(23)
+#define	CAPAB_33V			BIT(24)
+#define	CAPAB_30V			BIT(25)
+#define	CAPAB_18V			BIT(26)
+#define	CAPAB_VOLTS			(CAPAB_33V | CAPAB_30V | CAPAB_18V)
+#define	CAPAB_64BIT			BIT(28)
+
+/* REG_MAX_CURRENT bits */
+#define	MAX_CURRENT_33V_SHIFT		0
+#define	MAX_CURRENT_33V_MASK		(0xff << 0)
+#define	MAX_CURRENT_30V_SHIFT		8
+#define	MAX_CURRENT_30V_MASK		(0xff << 8)
+#define	MAX_CURRENT_18V_SHIFT		16
+#define	MAX_CURRENT_18V_MASK		(0xff << 16)
+
+/* REG_VERSION bits */
+#define	VERSION_VENDOR_SHIFT		8
+#define	VERSION_VENDOR_MASK		(0xff << 8)
+#define	VERSION_SDHOST_MASK		0xff
+#define	VERSION_SDHOST_1		0
+#define	VERSION_SDHOST_2		1
+
+/* REG_ADMA_ERROR bits */
+#define	ADMA_ERROR_STATE_ST_STOP	0
+#define	ADMA_ERROR_STATE_ST_FDS		1
+#define	ADMA_ERROR_STATE_ST_TFR		3
+#define	ADMA_ERROR_STATE_MASK		0x3
+#define	ADMA_ERROR_LEN_MISMATCH		BIT(2)
+
+#endif	/* _SYS_SDCARD_SDHOST_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,1058 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/sdcard/sda.h>
+#include <sys/note.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include "wbsd.h"
+
+typedef enum wbsd_direction { READ, WRITE } wbsd_direction_t;
+
+/*
+ * Soft state.
+ */
+typedef struct wbsd {
+	dev_info_t		*w_dip;
+	sda_host_t		*w_host;
+	ddi_intr_handle_t	w_ihandle;
+	ddi_softint_handle_t	w_shandle;
+	ddi_acc_handle_t	w_acch;
+	uint8_t			*w_regs;
+	kmutex_t		w_lock;
+	boolean_t		w_suspended;
+	uint8_t			w_width;	/* data bus width */
+	uint32_t		w_resid;	/* bytes remaining to xfer */
+	uint32_t		w_nblks;	/* blocks remaining to xfer */
+	uint32_t		w_blksz;	/* block size */
+	uint8_t			*w_data;	/* data pointer */
+	wbsd_direction_t	w_direction;
+	sda_err_t		w_cmd_err;
+	sda_err_t		w_dat_err;
+	boolean_t		w_done;
+	boolean_t		w_detect;
+	boolean_t		w_acmd12;
+	boolean_t		w_do_soft;
+	uint16_t		w_ctime;	/* command timeout (us) */
+} wbsd_t;
+
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wbsd::w_ctime))
+
+#define	GETREG(wp, r)		ddi_get8(wp->w_acch, wp->w_regs + r)
+#define	PUTREG(wp, r, v)	ddi_put8(wp->w_acch, wp->w_regs + r, v)
+#define	SETREG(wp, r, b)	PUTREG(wp, r, GETREG(wp, r) | b)
+#define	CLRREG(wp, r, b)	PUTREG(wp, r, GETREG(wp, r) & ~b)
+#define	GETIDX(wp, i, v)	\
+	{ PUTREG(wp, REG_IDXR, i); v = GETREG(wp, REG_DATAR); }
+#define	PUTIDX(wp, i, v)	\
+	{ PUTREG(wp, REG_IDXR, i); PUTREG(wp, REG_DATAR, v); }
+
+static int wbsd_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
+static int wbsd_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
+
+static int wbsd_attach(dev_info_t *);
+static int wbsd_detach(dev_info_t *);
+static int wbsd_resume(dev_info_t *);
+static int wbsd_suspend(dev_info_t *);
+
+static sda_err_t wbsd_cmd(void *, sda_cmd_t *);
+static sda_err_t wbsd_getprop(void *, sda_prop_t, uint32_t *);
+static sda_err_t wbsd_setprop(void *, sda_prop_t, uint32_t);
+static sda_err_t wbsd_reset(void *);
+static sda_err_t wbsd_halt(void *);
+static sda_err_t wbsd_poll(void *);
+
+static uint_t wbsd_hard_intr(caddr_t, caddr_t);
+static uint_t wbsd_soft_intr(caddr_t, caddr_t);
+static int wbsd_setup_interrupts(wbsd_t *);
+static void wbsd_teardown_interrupts(wbsd_t *);
+static void wbsd_fifo_read(wbsd_t *);
+static void wbsd_fifo_write(wbsd_t *);
+static void wbsd_reset_hw(wbsd_t *);
+static void wbsd_halt_hw(wbsd_t *);
+static void wbsd_send_stop(wbsd_t *);
+static void wbsd_busy_end(wbsd_t *);
+static void wbsd_prog_end(wbsd_t *);
+static void wbsd_detect(wbsd_t *);
+static void wbsd_error(wbsd_t *, sda_err_t);
+
+static struct dev_ops wbsd_dev_ops = {
+	DEVO_REV,			/* devo_rev */
+	0,				/* devo_refcnt */
+	ddi_no_info,			/* devo_getinfo */
+	nulldev,			/* devo_identify */
+	nulldev,			/* devo_probe */
+	wbsd_ddi_attach,		/* devo_attach */
+	wbsd_ddi_detach,		/* devo_detach */
+	nodev,				/* devo_reset */
+	NULL,				/* devo_cb_ops */
+	NULL,				/* devo_bus_ops */
+	NULL				/* devo_power */
+};
+
+static struct modldrv wbsd_modldrv = {
+	&mod_driverops,			/* drv_modops */
+	"Winbond W83L519D SD Host",	/* drv_linkinfo */
+	&wbsd_dev_ops			/* drv_dev_ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,			/* ml_rev */
+	{ &wbsd_modldrv, NULL }		/* ml_linkage */
+};
+
+static struct sda_ops wbsd_sda_ops = {
+	SDA_OPS_VERSION,
+	wbsd_cmd,			/* so_cmd */
+	wbsd_getprop,			/* so_getprop */
+	wbsd_setprop,			/* so_setprop */
+	wbsd_poll,			/* so_poll */
+	wbsd_reset,			/* so_reset */
+	wbsd_halt,			/* so_halt */
+};
+
+static ddi_device_acc_attr_t wbsd_regattr = {
+	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
+	DDI_NEVERSWAP_ACC,	/* devacc_attr_endian_flags */
+	DDI_STRICTORDER_ACC,	/* devacc_attr_dataorder */
+	DDI_DEFAULT_ACC,	/* devacc_attr_access */
+};
+
+int
+_init(void)
+{
+	int rv;
+
+	sda_host_init_ops(&wbsd_dev_ops);
+
+	if ((rv = mod_install(&modlinkage)) != 0) {
+		sda_host_fini_ops(&wbsd_dev_ops);
+	}
+
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int rv;
+
+	if ((rv = mod_remove(&modlinkage)) == 0) {
+		sda_host_fini_ops(&wbsd_dev_ops);
+	}
+
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+wbsd_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	switch (cmd) {
+	case DDI_ATTACH:
+		return (wbsd_attach(dip));
+	case DDI_RESUME:
+		return (wbsd_resume(dip));
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+int
+wbsd_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	switch (cmd) {
+	case DDI_DETACH:
+		return (wbsd_detach(dip));
+	case DDI_SUSPEND:
+		return (wbsd_suspend(dip));
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+int
+wbsd_attach(dev_info_t *dip)
+{
+	wbsd_t	*wp;
+
+	wp = kmem_zalloc(sizeof (*wp), KM_SLEEP);
+
+	wp->w_host = sda_host_alloc(dip, 1, &wbsd_sda_ops, NULL);
+	if (wp->w_host == NULL) {
+		cmn_err(CE_WARN, "Unable to allocate SDA host structure");
+		goto failed;
+	}
+	ddi_set_driver_private(dip, wp);
+	sda_host_set_private(wp->w_host, 0, wp);
+
+	wp->w_dip = dip;
+
+	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&wp->w_regs, 0, 0,
+	    &wbsd_regattr, &wp->w_acch) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "Unable to map registers");
+		goto failed;
+	}
+
+	/* make sure interrupts are disabled */
+	PUTREG(wp, REG_EIR, 0);
+
+	/* setup interrupts, also initializes locks */
+	if (wbsd_setup_interrupts(wp) != DDI_SUCCESS)
+		goto failed;
+
+	ddi_report_dev(dip);
+
+	/* enable device interrupts in DDI */
+	(void) ddi_intr_enable(wp->w_ihandle);
+
+	/* attach to the framework */
+	if (sda_host_attach(wp->w_host) != DDI_SUCCESS) {
+		goto failed;
+	}
+
+	return (DDI_SUCCESS);
+
+failed:
+	/* tear down interrupts */
+	if (wp->w_ihandle != NULL) {
+		PUTREG(wp, REG_EIR, 0);
+		(void) GETREG(wp, REG_ISR);
+
+		wbsd_teardown_interrupts(wp);
+	}
+
+	/* toss register map */
+	if (wp->w_regs != NULL) {
+		ddi_regs_map_free(&wp->w_acch);
+	}
+
+	/* free host resources */
+	if (wp->w_host != NULL) {
+		sda_host_free(wp->w_host);
+	}
+
+	kmem_free(wp, sizeof (*wp));
+	return (DDI_FAILURE);
+}
+
+int
+wbsd_detach(dev_info_t *dip)
+{
+	wbsd_t	*wp;
+
+	if ((wp = ddi_get_driver_private(dip)) == NULL) {
+		cmn_err(CE_WARN, "Unable to get soft state");
+		return (DDI_FAILURE);
+	}
+
+
+	sda_host_detach(wp->w_host);
+
+	/* disable interrupts */
+	PUTREG(wp, REG_EIR, 0);
+	(void) GETREG(wp, REG_ISR);
+
+	/* remove power from the socket */
+	SETREG(wp, REG_CSR,  CSR_POWER_N);
+
+	wbsd_teardown_interrupts(wp);
+	ddi_regs_map_free(&wp->w_acch);
+	kmem_free(wp, sizeof (*wp));
+	return (DDI_SUCCESS);
+}
+
+int
+wbsd_suspend(dev_info_t *dip)
+{
+	wbsd_t	*wp;
+
+	if ((wp = ddi_get_driver_private(dip)) == NULL) {
+		cmn_err(CE_WARN, "Unable to get soft state");
+		return (DDI_FAILURE);
+	}
+	mutex_enter(&wp->w_lock);
+	wp->w_suspended = B_TRUE;
+	wbsd_halt_hw(wp);
+	mutex_exit(&wp->w_lock);
+
+	return (DDI_SUCCESS);
+}
+
+int
+wbsd_resume(dev_info_t *dip)
+{
+	wbsd_t	*wp;
+
+	if ((wp = ddi_get_driver_private(dip)) == NULL) {
+		cmn_err(CE_WARN, "Unable to get soft state");
+		return (DDI_FAILURE);
+	}
+
+	mutex_enter(&wp->w_lock);
+	wp->w_suspended = B_FALSE;
+	wbsd_reset_hw(wp);
+	mutex_exit(&wp->w_lock);
+
+	sda_host_detect(wp->w_host, 0);
+
+	return (DDI_SUCCESS);
+}
+
+int
+wbsd_setup_interrupts(wbsd_t *wp)
+{
+	uint_t			ipri;
+	int			actual;
+	ddi_intr_handle_t	ih;
+	ddi_softint_handle_t	sh;
+
+	/*
+	 * Setup interrupt.  Note that these are ISA devices, and only have
+	 * a single fixed interrupt.
+	 */
+	if (ddi_intr_alloc(wp->w_dip, &ih, DDI_INTR_TYPE_FIXED, 0,
+	    1, &actual, DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "Unable to allocate interrupt");
+		return (DDI_FAILURE);
+	}
+	if (ddi_intr_get_pri(ih, &ipri) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "Unable to get interrupt priority");
+		(void) ddi_intr_free(ih);
+		return (DDI_FAILURE);
+	}
+	if (ddi_intr_add_handler(ih, wbsd_hard_intr, wp, NULL) !=
+	    DDI_SUCCESS) {
+		cmn_err(CE_WARN, "Unable to add interrupt handler");
+		(void) ddi_intr_free(ih);
+		return (DDI_FAILURE);
+	}
+	mutex_init(&wp->w_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
+
+	/*
+	 * Soft interrupt is next.
+	 */
+	if (ddi_intr_add_softint(wp->w_dip, &sh, DDI_INTR_SOFTPRI_MIN,
+	    wbsd_soft_intr, wp) != DDI_SUCCESS) {
+		(void) ddi_intr_remove_handler(ih);
+		(void) ddi_intr_free(ih);
+		mutex_destroy(&wp->w_lock);
+		return (DDI_FAILURE);
+	}
+
+	wp->w_ihandle = ih;
+	wp->w_shandle = sh;
+
+	return (DDI_SUCCESS);
+}
+
+void
+wbsd_teardown_interrupts(wbsd_t *wp)
+{
+	(void) ddi_intr_disable(wp->w_ihandle);
+
+	/*
+	 * These are here to ensure that any previously
+	 * running interrupts (hard or soft) have completed.
+	 */
+	mutex_enter(&wp->w_lock);
+	mutex_exit(&wp->w_lock);
+
+	(void) ddi_intr_remove_handler(wp->w_ihandle);
+	(void) ddi_intr_free(wp->w_ihandle);
+	(void) ddi_intr_remove_softint(wp->w_shandle);
+
+	mutex_destroy(&wp->w_lock);
+}
+
+
+void
+wbsd_detect(wbsd_t *wp)
+{
+	wp->w_detect = B_TRUE;
+	wp->w_done = B_TRUE;
+	wp->w_cmd_err = wp->w_dat_err = SDA_ENODEV;
+	wp->w_do_soft = B_TRUE;
+}
+
+void
+wbsd_error(wbsd_t *wp, sda_err_t err)
+{
+	wp->w_done = B_TRUE;
+	wp->w_cmd_err = wp->w_dat_err = err;
+	wp->w_do_soft = B_TRUE;
+}
+
+void
+wbsd_busy_end(wbsd_t *wp)
+{
+	wp->w_done = B_TRUE;
+	wp->w_do_soft = B_TRUE;
+}
+
+void
+wbsd_prog_end(wbsd_t *wp)
+{
+	ASSERT(wp->w_direction == WRITE);
+	if (wp->w_nblks > 0) {
+		wp->w_nblks--;
+		if (wp->w_nblks > 0) {
+
+			/*
+			 * Start transferring the next block.
+			 */
+			wp->w_resid = wp->w_blksz;
+			wbsd_fifo_write(wp);
+
+		} else {
+			/*
+			 * If we needed auto terminate, then do it now.
+			 */
+			if (wp->w_acmd12) {
+				wbsd_send_stop(wp);
+
+			/*
+			 * Otherwise its a single block write completion, so
+			 * just complete the transfer.
+			 */
+			} else {
+				wp->w_done = B_TRUE;
+				wp->w_do_soft = B_TRUE;
+			}
+		}
+	}
+}
+
+uint_t
+wbsd_hard_intr(caddr_t arg1, caddr_t arg2)
+{
+	wbsd_t		*wp = (void *)arg1;
+	uint8_t		isr;
+	uint_t		rv = DDI_INTR_UNCLAIMED;
+	boolean_t	do_soft = B_FALSE;
+	int		i;
+
+	mutex_enter(&wp->w_lock);
+	if (wp->w_suspended) {
+		mutex_exit(&wp->w_lock);
+		return (rv);
+	}
+
+	for (i = 0; i < 100000; i++) {
+		isr = GETREG(wp, REG_ISR);
+
+		if ((isr == 0xff) || ((isr & ISR_WANTED) == 0))
+			break;
+
+		rv = DDI_INTR_CLAIMED;
+
+		if (isr & ISR_CARD) {
+			/*
+			 * Make sure that the chip is fully reset after
+			 * a card interrupt occurs.
+			 */
+			wbsd_reset_hw(wp);
+			wbsd_detect(wp);
+			break;
+		}
+
+		if (isr & ISR_FIFO) {
+			/*
+			 * FIFO data ready.  Process this as quickly as
+			 * possible.
+			 */
+			if (wp->w_direction == WRITE) {
+				wbsd_fifo_write(wp);
+			} else {
+				wbsd_fifo_read(wp);
+			}
+		}
+
+		if (isr & ISR_BUSY_END) {
+			wbsd_busy_end(wp);
+		}
+
+		if (isr & ISR_PROG_END) {
+			wbsd_prog_end(wp);
+		}
+
+		if (isr & ISR_TIMEOUT) {
+			wbsd_error(wp, SDA_ETIME);
+		}
+
+		if (isr & ISR_CRC_ERR) {
+			wbsd_error(wp, SDA_ECRC7);
+		}
+	}
+
+	if (i >= 100000) {
+		PUTREG(wp, REG_EIR, 0);
+		sda_host_log(wp->w_host, 0,
+		    "Stuck interrupt detected (isr %x)", isr);
+		sda_host_fault(wp->w_host, 0, SDA_FAULT_HOST);
+	}
+
+	/*
+	 * If arg2 is NULL, then we are running as an ordinary interrupt.
+	 * Otherwise we are running from polled context, and cannot trigger
+	 * soft interrupts.
+	 */
+	if (wp->w_do_soft && (arg2 == NULL)) {
+		wp->w_do_soft = B_FALSE;
+		do_soft = B_TRUE;
+	}
+	mutex_exit(&wp->w_lock);
+
+	if (do_soft)
+		(void) ddi_intr_trigger_softint(wp->w_shandle, NULL);
+
+	return (rv);
+}
+
+/*ARGSUSED1*/
+uint_t
+wbsd_soft_intr(caddr_t arg1, caddr_t arg2)
+{
+	wbsd_t 		*wp = (void *)arg1;
+	boolean_t	detect = B_FALSE;
+	boolean_t	done = B_FALSE;
+	sda_err_t	err = SDA_EOK;
+
+	mutex_enter(&wp->w_lock);
+
+	detect = wp->w_detect;
+	done = wp->w_done;
+	err = wp->w_dat_err;
+
+	wp->w_done = B_FALSE;
+	wp->w_detect = B_FALSE;
+	wp->w_dat_err = SDA_EOK;
+
+	mutex_exit(&wp->w_lock);
+
+	if (detect) {
+		sda_host_detect(wp->w_host, 0);
+	}
+
+	if (done) {
+		sda_host_transfer(wp->w_host, 0, err);
+	}
+
+	return (DDI_INTR_CLAIMED);
+}
+
+sda_err_t
+wbsd_poll(void *arg)
+{
+	/* 2nd argument indicates running from poll */
+	(void) wbsd_hard_intr(arg, (void *)wbsd_poll);
+	(void) wbsd_soft_intr(arg, (void *)wbsd_poll);
+	return (SDA_EOK);
+}
+
+sda_err_t
+wbsd_getprop(void *arg, sda_prop_t prop, uint32_t *val)
+{
+	wbsd_t		*wp = arg;
+	sda_err_t	rv = SDA_EOK;
+	uint8_t		clock;
+
+	mutex_enter(&wp->w_lock);
+	if (wp->w_suspended) {
+		mutex_exit(&wp->w_lock);
+		return (SDA_ESUSPENDED);
+	}
+
+	switch (prop) {
+	case SDA_PROP_INSERTED:
+		*val = (GETREG(wp, REG_CSR) & CSR_PRESENT) ? B_TRUE : B_FALSE;
+		break;
+
+	case SDA_PROP_WPROTECT:
+		/* switch signal select */
+		SETREG(wp, REG_CSR, CSR_MSLED);
+
+		drv_usecwait(1000);
+		*val = (GETREG(wp, REG_CSR) & CSR_WPROTECT) ? B_TRUE : B_FALSE;
+		CLRREG(wp, REG_CSR, CSR_MSLED);
+		break;
+
+	case SDA_PROP_OCR:
+		*val = OCR_32_33V;
+		break;
+
+	case SDA_PROP_CLOCK:
+		GETIDX(wp, IDX_CLOCK, clock)
+		switch (clock) {
+		case IDX_CLOCK_24M:
+			*val = 24000000;
+			break;
+		case IDX_CLOCK_16M:
+			*val = 16000000;
+			break;
+		case IDX_CLOCK_12M:
+			*val = 12000000;
+			break;
+		case IDX_CLOCK_375K:
+			*val = 375000;
+			break;
+		default:
+			*val = 0;
+			break;
+		}
+		break;
+
+	case SDA_PROP_CAP_4BITS:
+		/*
+		 * On Tadpole SPARCLE hardware, the card detect uses
+		 * DAT3, which causes all kinds of problems.  It is quite
+		 * troublesome to support card detection events properly with
+		 * this configuration, so we fall back to supporting only
+		 * single bit mode.  It is possible to correct this, but
+		 * it requires changes in the framework, particularly to
+		 * note that the DAT3 pin is used this way.
+		 *
+		 * In particular, this would require separate commands to
+		 * the card to connect/disconnect the card internal pullup
+		 * resistor, as well as to manipulate the interrupt register,
+		 * and then poll card status on command completion.
+		 *
+		 * The Winbond part is so slow, that it is doubtful that the
+		 * trouble would be worth it.  On x86 hardware where we can
+		 * make use of GPIO pin detection, the situation might be
+		 * quite different.
+		 */
+		*val = B_FALSE;
+		break;
+
+	case SDA_PROP_CAP_NOPIO:
+	case SDA_PROP_CAP_INTR:
+	case SDA_PROP_CAP_8BITS:
+		*val = B_FALSE;
+		break;
+
+	default:
+		rv = SDA_ENOTSUP;
+		break;
+	}
+
+	mutex_exit(&wp->w_lock);
+
+	return (rv);
+}
+
+sda_err_t
+wbsd_setprop(void *arg, sda_prop_t prop, uint32_t val)
+{
+	wbsd_t		*wp = arg;
+	sda_err_t	rv = SDA_EOK;
+	uint8_t		clock;
+
+	mutex_enter(&wp->w_lock);
+	if (wp->w_suspended) {
+		mutex_exit(&wp->w_lock);
+		return (SDA_ESUSPENDED);
+	}
+
+	switch (prop) {
+
+	case SDA_PROP_LED:
+		break;
+	case SDA_PROP_CLOCK:
+		/*
+		 * Note that the "worst case" command timeouts are 16.7us for
+		 * the "slow" 12MHz clock.  So a 20us timeout is enough for
+		 * everything faster.
+		 */
+		wp->w_ctime = 20;
+		if (val >= 24000000) {
+			clock = IDX_CLOCK_24M;
+		} else if (val >= 16000000) {
+			clock = IDX_CLOCK_16M;
+		} else if (val >= 12000000) {
+			clock = IDX_CLOCK_12M;
+		} else {
+			/*
+			 * Worst case command timeout is 533.3 usec.  Just
+			 * pick a big enough value to force it.  If we choose
+			 * a value of 2 msec, it is enough even if the clock
+			 * runs as low as 100KHz.
+			 */
+			clock = IDX_CLOCK_375K;
+			wp->w_ctime = 2000;
+		}
+		PUTIDX(wp, IDX_CLOCK, clock);
+		break;
+
+	case SDA_PROP_BUSWIDTH:
+		/*
+		 * See the comment in SDA_PROP_CAP_4BITS, though.
+		 */
+		if ((val == 4) || (val == 1)) {
+			wp->w_width = (uint8_t)val;
+		} else {
+			rv = SDA_EINVAL;
+		}
+		break;
+
+	case SDA_PROP_OCR:
+		if ((val == OCR_32_33V) &&
+		    ((GETREG(wp, REG_CSR) & CSR_PRESENT) != 0)) {
+			/* apply power */
+			CLRREG(wp, REG_CSR, CSR_POWER_N);
+			/* activate the various other interrupts on the chip */
+			PUTREG(wp, REG_EIR, EIR_CARD | EIR_FIFO | EIR_CRC_ERR |
+			    EIR_TIMEOUT | EIR_PROG_END | EIR_BUSY_END);
+		} else {
+			/* power down and reset */
+			wbsd_reset_hw(wp);
+			if (val != 0) {
+				rv = SDA_EINVAL;
+			}
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	mutex_exit(&wp->w_lock);
+
+	return (rv);
+}
+
+void
+wbsd_fifo_read(wbsd_t *wp)
+{
+	uint8_t	fsr;
+	uint8_t	cnt;
+
+	ASSERT(mutex_owned(&wp->w_lock));
+
+	while ((((fsr = GETREG(wp, REG_FSR)) & FSR_EMPTY) == 0) &&
+	    (wp->w_resid != 0)) {
+		/*
+		 * The point of this logic is to avoid extra reads of
+		 * the fifo status register.  We are throughput
+		 * limited by the number of PIOs.
+		 */
+		if ((fsr & FSR_FULL) != 0) {
+			cnt = 16;
+		} else if ((fsr & FSR_FULL_THRE) != 0) {
+			cnt = 8;
+		} else {
+			cnt = 1;
+		}
+		while ((cnt != 0) && (wp->w_resid != 0)) {
+			cnt--;
+			wp->w_resid--;
+			*wp->w_data++ = GETREG(wp, REG_DFR);
+		}
+	}
+
+	if (wp->w_resid != 0) {
+		PUTIDX(wp, IDX_THRESH, IDX_THRESH_FULL | min(wp->w_resid, 8));
+	} else {
+		PUTIDX(wp, IDX_THRESH, 0);
+
+		if (wp->w_acmd12) {
+			wbsd_send_stop(wp);
+		} else {
+			wbsd_busy_end(wp);
+		}
+	}
+}
+
+void
+wbsd_fifo_write(wbsd_t *wp)
+{
+	uint8_t	fsr;
+	uint8_t	cnt;
+
+	ASSERT(mutex_owned(&wp->w_lock));
+
+	while ((((fsr = GETREG(wp, REG_FSR)) & FSR_FULL) == 0) &&
+	    (wp->w_resid != 0)) {
+		if ((fsr & FSR_EMPTY) != 0) {
+			cnt = 16;
+		} else if ((fsr & FSR_EMPTY_THRE) != 0) {
+			cnt = 8;
+		} else {
+			cnt = 1;
+		}
+		while ((cnt != 0) && (wp->w_resid != 0)) {
+			cnt--;
+			wp->w_resid--;
+			PUTREG(wp, REG_DFR, *wp->w_data++);
+		}
+	}
+	if (wp->w_resid != 0) {
+		PUTIDX(wp, IDX_THRESH, IDX_THRESH_EMPTY | min(wp->w_resid, 8));
+	} else {
+		PUTIDX(wp, IDX_THRESH, 0);
+		/* wait for PROG interrupt */
+	}
+}
+
+void
+wbsd_send_stop(wbsd_t *wp)
+{
+	wp->w_acmd12 = B_FALSE;
+
+	PUTREG(wp, REG_CMDR, CMD_STOP_TRANSMIT);
+	PUTREG(wp, REG_CMDR, 0);
+	PUTREG(wp, REG_CMDR, 0);
+	PUTREG(wp, REG_CMDR, 0);
+	PUTREG(wp, REG_CMDR, 0);
+}
+
+sda_err_t
+wbsd_cmd(void *arg, sda_cmd_t *cmdp)
+{
+	wbsd_t		*wp = arg;
+	boolean_t	checkcrc;
+	uint8_t		rstart;
+	uint8_t		rwords;
+	sda_err_t	rv = SDA_EOK;
+
+	checkcrc = B_TRUE;
+	rstart = IDX_RESP_12;
+	rwords = 1;
+
+	switch (cmdp->sc_rtype) {
+	case R0:
+		rwords = 0;
+		break;
+	case R1:
+	case R5:
+	case R6:
+	case R7:
+	case R1b:
+	case R5b:
+		break;
+	case R2:
+		rstart = IDX_RESP_1;
+		rwords = 4;
+		checkcrc = B_FALSE;
+		break;
+	case R3:
+	case R4:
+		checkcrc = B_FALSE;
+		break;
+	}
+
+	mutex_enter(&wp->w_lock);
+	if (wp->w_suspended) {
+		mutex_exit(&wp->w_lock);
+		return (SDA_ESUSPENDED);
+	}
+
+	if (cmdp->sc_nblks != 0) {
+		uint16_t	sz;
+		uint8_t		v;
+
+		wp->w_blksz = cmdp->sc_blksz;
+		wp->w_nblks = cmdp->sc_nblks;
+
+		/* save a few things for completion */
+		wp->w_data = (uint8_t *)cmdp->sc_kvaddr;
+
+		wp->w_acmd12 = (cmdp->sc_flags & SDA_CMDF_AUTO_CMD12) ?
+		    B_TRUE : B_FALSE;
+
+		/* maximum timeouts, 127 msec and 25500 cycles */
+		PUTIDX(wp, IDX_TAAC, 127);
+		PUTIDX(wp, IDX_NSAC, 255);
+
+		/* set data width */
+		sz = cmdp->sc_blksz + ((wp->w_width == 4) ? 8 : 2);
+		PUTIDX(wp, IDX_BLKSZMSB, ((sz >> 4) & 0xf0) |
+		    ((wp->w_width == 4) ? 1 : 0));
+		PUTIDX(wp, IDX_BLKSZLSB, sz & 0xff);
+
+		/* make sure start the fifo with a clean slate */
+		GETIDX(wp, IDX_RESET, v);
+		v |= IDX_RESET_FIFO;
+		PUTIDX(wp, IDX_RESET, v);
+
+		/* we don't use DMA, period */
+		PUTIDX(wp, IDX_DMA, 0);
+
+		if ((cmdp->sc_flags & SDA_CMDF_READ) != 0) {
+			/*
+			 * Reading... we arrange to wait for the full
+			 * transfer, than doing a block at a time.
+			 * Simpler that way.
+			 */
+
+			wp->w_direction = READ;
+			wp->w_resid = wp->w_blksz * wp->w_nblks;
+			PUTIDX(wp, IDX_THRESH, IDX_THRESH_FULL |
+			    min(wp->w_resid, 8));
+		} else {
+			/*
+			 * Writing... go ahead and prefill the fifo.
+			 * We write a block at a time, because we need
+			 * the PROG interrupts in the block gaps.
+			 */
+
+			wp->w_direction = WRITE;
+			wp->w_resid = wp->w_blksz;
+			PUTIDX(wp, IDX_THRESH, IDX_THRESH_EMPTY |
+			    min(wp->w_blksz, 8));
+			wbsd_fifo_write(wp);
+		}
+	}
+
+	/*
+	 * This chip is a bit simple minded.  It cannot distinguish
+	 * between errors that occur on the data line, and those that
+	 * occur on the CMD line.
+	 */
+
+	/* make sure we clear any preexisting error condition */
+	wp->w_cmd_err = SDA_EOK;
+
+	PUTREG(wp, REG_CMDR, cmdp->sc_index);
+	PUTREG(wp, REG_CMDR, (cmdp->sc_argument >> 24) & 0xff);
+	PUTREG(wp, REG_CMDR, (cmdp->sc_argument >> 16) & 0xff);
+	PUTREG(wp, REG_CMDR, (cmdp->sc_argument >> 8) & 0xff);
+	PUTREG(wp, REG_CMDR, (cmdp->sc_argument) & 0xff);
+
+	/*
+	 * Note that while we are waiting for the timer to run out (which
+	 * is really short), a timeout or other error interrupt can occur.
+	 * We want to know about such error indications, so we have to drop
+	 * to the lock so that the interrupt service routine can post the
+	 * appropriate error in the w_cmd_err variable.
+	 */
+	mutex_exit(&wp->w_lock);
+	drv_usecwait(wp->w_ctime);
+	mutex_enter(&wp->w_lock);
+
+	if ((rv = wp->w_cmd_err) == SDA_EOK) {
+		uint8_t	stat;
+		GETIDX(wp, IDX_STATUS, stat);
+		if ((stat & IDX_STATUS_TRAFFIC) != 0) {
+			rv = SDA_ETIME;
+		}
+	}
+
+	/* some commands don't use valid CRC */
+	if ((rv == SDA_ECRC7) && !checkcrc) {
+		rv = SDA_EOK;
+	}
+
+	PUTIDX(wp, IDX_RESET, IDX_RESET_AUTO_INC);
+	PUTREG(wp, REG_IDXR, rstart);
+	while (rwords != 0) {
+		uint32_t	v;
+		v = GETREG(wp, REG_DATAR);
+		v <<= 8;
+		v |= GETREG(wp, REG_DATAR);
+		v <<= 8;
+		v |= GETREG(wp, REG_DATAR);
+		v <<= 8;
+		v |= GETREG(wp, REG_DATAR);
+		rwords--;
+		cmdp->sc_response[rwords] = v;
+	}
+	PUTIDX(wp, IDX_RESET, IDX_RESET_AUTO_INC);
+
+	mutex_exit(&wp->w_lock);
+
+
+	return (rv);
+}
+
+void
+wbsd_halt_hw(wbsd_t *wp)
+{
+	/* reset chip and fifo */
+	PUTIDX(wp, IDX_RESET, IDX_RESET_SOFT | IDX_RESET_FIFO);
+
+	/* disable interrupts */
+	PUTREG(wp, REG_EIR, 0);
+
+	/* remove power */
+	SETREG(wp, REG_CSR, CSR_POWER_N);
+}
+
+void
+wbsd_reset_hw(wbsd_t *wp)
+{
+	/* remove power from slot, set LED enable */
+	PUTREG(wp, REG_CSR, CSR_POWER_N);
+
+	/* reset chip and fifo */
+	PUTIDX(wp, IDX_RESET, IDX_RESET_SOFT | IDX_RESET_FIFO);
+
+	/* clear any pending interrupts */
+	(void) GETREG(wp, REG_ISR);
+
+	/* enable card interrupt */
+	PUTREG(wp, REG_EIR, EIR_CARD);
+}
+
+sda_err_t
+wbsd_reset(void *arg)
+{
+	wbsd_t	*wp = arg;
+
+	mutex_enter(&wp->w_lock);
+	wp->w_acmd12 = B_FALSE;
+	wp->w_resid = 0;
+	wp->w_data = NULL;
+	wp->w_width = 1;
+
+	if (!wp->w_suspended) {
+		/* reset occurred when we suspended */
+		wbsd_reset_hw(wp);
+	}
+	mutex_exit(&wp->w_lock);
+
+	return (SDA_EOK);
+}
+
+sda_err_t
+wbsd_halt(void *arg)
+{
+	wbsd_t	*wp = arg;
+
+	mutex_enter(&wp->w_lock);
+	if (!wp->w_suspended) {
+		wbsd_halt_hw(wp);
+	}
+	mutex_exit(&wp->w_lock);
+
+	return (SDA_EOK);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.h	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,154 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SDCARD_WBSD_H
+#define	_SYS_SDCARD_WBSD_H
+
+/*
+ * Private header for the Winbond W83L519D series SD controller.
+ */
+
+/*
+ * Direct access registers.
+ */
+#define	REG_CMDR	0x00	/* command register */
+#define	REG_DFR		0x01	/* data fifo register */
+#define	REG_EIR		0x02	/* enable interrupt register */
+#define	REG_ISR		0x03	/* interrupt status register */
+#define	REG_FSR		0x04	/* fifo status register */
+#define	REG_IDXR	0x05	/* index register */
+#define	REG_DATAR	0x06	/* data register */
+#define	REG_CSR		0x07	/* card status register */
+
+/*
+ * Direct access register values.
+ */
+
+/*
+ * Note that some sources appear to have mixed up the busy and prog
+ * bits.  At least on a Tadpole SPARCLE the bits seem to work as
+ * defined here, although note that on SPARC hardware there does not
+ * appear to be any kind of DMA support for ebus (ISA).
+ */
+#define	EIR_CARD	0x40	/* card interrupt */
+#define	EIR_FIFO	0x20	/* FIFO threshold reached */
+#define	EIR_CRC_ERR	0x10	/* CRC error? */
+#define	EIR_TIMEOUT	0x08	/* timeout on CMD or DAT */
+#define	EIR_BUSY_END	0x04	/* programming complete */
+#define	EIR_PROG_END	0x02	/* busy bit has cleared */
+#define	EIR_TC		0x01	/* DMA transfer complete */
+#define	EIR_TYPICAL	(EIR_CARD | EIR_CRC_ERR | EIR_TIMEOUT)
+#define	EIR_WRITE	(EIR_TYPICAL | EIR_FIFO | EIR_PROG_END)
+#define	EIR_READ	(EIR_TYPICAL | EIR_FIFO)
+#define	EIR_STOP	(EIR_TYPICAL | EIR_BUSY_END)
+
+#define	ISR_CARD	0x40	/* card interrupt */
+#define	ISR_FIFO	0x20	/* FIFO threshold reached */
+#define	ISR_CRC_ERR	0x10	/* CRC7 error */
+#define	ISR_TIMEOUT	0x08	/* timeout on CMD or DAT */
+#define	ISR_BUSY_END	0x04	/* programming complete */
+#define	ISR_PROG_END	0x02	/* busy bit has cleared */
+#define	ISR_TC		0x01	/* DMA transfer complete */
+#define	ISR_WANTED	(ISR_CARD | ISR_FIFO | ISR_CRC_ERR | ISR_TIMEOUT | \
+			ISR_BUSY_END | ISR_PROG_END)
+
+#define	FSR_FULL_THRE	0x10
+#define	FSR_EMPTY_THRE	0x20
+#define	FSR_FULL	0x40
+#define	FSR_EMPTY	0x80
+#define	FSR_PTR_MASK	0x0F
+
+#define	CSR_PRESENT	0x01
+#define	CSR_WPROTECT	0x04
+#define	CSR_POWER_N	0x10
+#define	CSR_MSLED	0x20
+
+/*
+ * Index offsets for indirect registers.
+ */
+#define	IDX_CLOCK	0x01	/* clock select */
+#define	IDX_BLKSZMSB	0x02	/* data width, block size MSB */
+#define	IDX_TAAC	0x03	/* TAAC timing spec */
+#define	IDX_NSAC	0x04	/* NSAC timing spec */
+#define	IDX_BLKSZLSB	0x05	/* block size LSB */
+#define	IDX_RESET	0x06	/* reset */
+#define	IDX_DMA		0x07	/* DMA setting */
+#define	IDX_THRESH	0x08	/* FIFO threshold control */
+#define	IDX_PID_1	0x0E	/* product id */
+#define	IDX_PID_2	0x0F	/* product id */
+#define	IDX_STATUS	0x10	/* chip status */
+#define	IDX_CMD		0x11	/* first command index */
+#define	IDX_RESP_TYPE	0x1E
+#define	IDX_RESP_0	0x1F
+#define	IDX_RESP_1	0x20
+#define	IDX_RESP_2	0x21
+#define	IDX_RESP_3	0x22
+#define	IDX_RESP_4	0x13
+#define	IDX_RESP_5	0x24
+#define	IDX_RESP_6	0x25
+#define	IDX_RESP_7	0x26
+#define	IDX_RESP_8	0x27
+#define	IDX_RESP_9	0x28
+#define	IDX_RESP_10	0x29
+#define	IDX_RESP_11	0x2A
+#define	IDX_RESP_12	0x2B
+#define	IDX_RESP_13	0x2C
+#define	IDX_RESP_14	0x2D
+#define	IDX_RESP_15	0x2E
+#define	IDX_RESP_16	0x2F
+#define	IDX_CRCSTAT	0x30
+
+#define	IDX_CLOCK_375K		0	/* clock/128 */
+#define	IDX_CLOCK_12M		1	/* clock/4 */
+#define	IDX_CLOCK_16M		2	/* clock/3 */
+#define	IDX_CLOCK_24M		3	/* clock/2 */
+
+#define	IDX_RESET_DAT3_H	0x08
+#define	IDX_RESET_FIFO		0x04
+#define	IDX_RESET_SOFT		0x02
+#define	IDX_RESET_AUTO_INC	0x01	/* not really a reset bit */
+
+#define	IDX_DMA_EN		0x02
+#define	IDX_DMA_SINGLE		0x01
+
+#define	IDX_STATUS_READ		0x80	/* block write in progress */
+#define	IDX_STATUS_WRITE	0x40	/* block read in progress */
+#define	IDX_STATUS_BUSY		0x20	/* e.g. R1b or R5b */
+#define	IDX_STATUS_DAT		0xE0	/* stats using DAT line */
+#define	IDX_STATUS_TRAFFIC	0x04	/* cmd line busy */
+#define	IDX_STATUS_CMD		0x02
+#define	IDX_STATUS_RESP		0x01
+
+#define	IDX_RESP_TYPE_LONG	0x01	/* the chip figures these out, btw */
+#define	IDX_RESP_TYPE_SHORT	0x00
+
+#define	IDX_CRC_MASK		0x1F
+#define	IDX_CRC_OK		0x05
+
+#define	IDX_THRESH_MASK		0x0F	/* threshold value (may not work) */
+#define	IDX_THRESH_FULL		0x10	/* enable threshold full */
+#define	IDX_THRESH_EMPTY	0x20	/* enable threshold empty */
+
+#endif	/* _SYS_SDCARD_WBSD_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/mapfile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+{
+	global:
+		# consolidation private nexus interfaces 
+		sda_host_init_ops;
+		sda_host_fini_ops;
+		sda_host_alloc;
+		sda_host_free;
+		sda_host_attach;
+		sda_host_detach;
+		sda_host_detect;
+		sda_host_set_private;
+		sda_host_transfer;
+		sda_host_fault;
+		sda_host_log;
+
+		# project private interfaces used by sdcard
+		sda_mem_init;
+		sda_mem_fini;
+
+	local:
+		*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/sda_cmd.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,355 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SD card common framework.  This module provides most of the common
+ * functionality so that SecureDigital host adapters and client devices
+ * (such as the sdcard driver) can share common code.
+ *
+ * NB that this file contains a fair bit of non-DDI compliant code.
+ * But writing a nexus driver would be impossible to do with only DDI
+ * compliant interfaces.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sdcard/sda_impl.h>
+
+/*
+ * Types and Structures.
+ */
+
+typedef struct sda_cmd_impl {
+	struct sda_cmd	c_public;
+
+	/*
+	 * Implementation private stuff.
+	 */
+	sda_slot_t	*c_slot;
+	kmutex_t	c_lock;
+	kcondvar_t	c_cv;
+	list_node_t	c_list;
+	sda_err_t	c_errno;
+
+	sda_index_t	c_acmd;		/* saved acmd */
+	sda_rtype_t	c_artype;	/* saved rtype */
+	uint32_t	c_aarg;		/* saved argument */
+
+	void		(*c_done)(struct sda_cmd *);
+	void		*c_private;
+} sda_cmd_impl_t;
+
+#define	c_index		c_public.sc_index
+#define	c_argument	c_public.sc_argument
+#define	c_rtype		c_public.sc_rtype
+#define	c_response	c_public.sc_response
+#define	c_blksz		c_public.sc_blksz
+#define	c_nblks		c_public.sc_nblks
+#define	c_resid		c_public.sc_resid
+#define	c_flags		c_public.sc_flags
+#define	c_ndmac		c_public.sc_ndmac
+#define	c_dmacs		c_public.sc_dmacs
+#define	c_kvaddr	c_public.sc_kvaddr
+
+/*
+ * Local Prototypes.
+ */
+
+static void sda_cmd_wait(sda_cmd_t *);
+static int sda_cmd_ctor(void *, void *, int);
+static void sda_cmd_dtor(void *, void *);
+
+/*
+ * Static Variables.
+ */
+
+static kmem_cache_t *sda_cmd_cache;
+
+/*
+ * Macros.
+ */
+
+#define	CIP(cmdp)	((sda_cmd_impl_t *)(void *)cmdp)
+
+/*
+ * Implementation.
+ */
+
+void
+sda_cmd_init(void)
+{
+	sda_cmd_cache = kmem_cache_create("sda_cmd_cache",
+	    sizeof (struct sda_cmd_impl), 0, sda_cmd_ctor, sda_cmd_dtor,
+	    NULL, NULL, NULL, 0);
+}
+
+void
+sda_cmd_fini(void)
+{
+	kmem_cache_destroy(sda_cmd_cache);
+}
+
+void
+sda_cmd_list_init(list_t *list)
+{
+	list_create(list, sizeof (struct sda_cmd_impl),
+	    offsetof(struct sda_cmd_impl, c_list));
+}
+
+void
+sda_cmd_list_fini(list_t *list)
+{
+	list_destroy(list);
+}
+
+/*ARGSUSED1*/
+int
+sda_cmd_ctor(void *cbuf, void *arg, int kmflags)
+{
+	sda_cmd_impl_t	*c = cbuf;
+
+	mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&c->c_cv, NULL, CV_DRIVER, NULL);
+	return (0);
+}
+
+/*ARGSUSED1*/
+void
+sda_cmd_dtor(void *cbuf, void *arg)
+{
+	sda_cmd_impl_t	*c = cbuf;
+
+	cv_destroy(&c->c_cv);
+	mutex_destroy(&c->c_lock);
+}
+
+void *
+sda_cmd_data(sda_cmd_t *cmdp)
+{
+	return (CIP(cmdp)->c_private);
+}
+
+sda_err_t
+sda_cmd_errno(sda_cmd_t *cmdp)
+{
+	return (CIP(cmdp)->c_errno);
+}
+
+void
+sda_cmd_notify(sda_cmd_t *cmdp, uint16_t flags, sda_err_t errno)
+{
+	sda_cmd_impl_t	*c = CIP(cmdp);
+
+	/*
+	 * Now we need to make sure that we wake anyone waiting on this
+	 * command to complete, if it is complete.
+	 */
+	mutex_enter(&c->c_lock);
+	c->c_flags &= ~(flags);
+	/*
+	 * Don't overwrite an earlier error.
+	 */
+	if (c->c_errno == SDA_EOK) {
+		c->c_errno = errno;
+	}
+	if ((c->c_flags & (SDA_CMDF_BUSY | SDA_CMDF_DAT)) == 0) {
+
+		if (c->c_done != NULL) {
+			mutex_exit(&c->c_lock);
+			c->c_done(cmdp);
+		} else {
+			cv_broadcast(&c->c_cv);
+			mutex_exit(&c->c_lock);
+		}
+	} else {
+		mutex_exit(&c->c_lock);
+	}
+}
+
+void
+sda_cmd_wait(sda_cmd_t *cmdp)
+{
+	sda_cmd_impl_t	*c = CIP(cmdp);
+
+	mutex_enter(&c->c_lock);
+	while ((c->c_flags & (SDA_CMDF_BUSY | SDA_CMDF_DAT)) != 0)
+		cv_wait(&c->c_cv, &c->c_lock);
+	mutex_exit(&c->c_lock);
+}
+
+void
+sda_cmd_submit(sda_slot_t *slot, sda_cmd_t *cmdp, void (*done)(sda_cmd_t *))
+{
+	sda_cmd_impl_t	*c = CIP(cmdp);
+	sda_err_t	errno = 0;
+
+	mutex_enter(&c->c_lock);
+	c->c_done = done;
+	c->c_flags |= SDA_CMDF_BUSY;
+	mutex_exit(&c->c_lock);
+
+	sda_slot_enter(slot);
+
+	/* checks for cases where the slot can't accept the command */
+	if (slot->s_failed) {
+		errno = SDA_EFAULT;
+	}
+	if (!slot->s_inserted) {
+		errno = SDA_ENODEV;
+	}
+	if (errno != SDA_EOK) {
+		sda_slot_exit(slot);
+		/* fail it synchronously */
+		sda_cmd_notify(cmdp, SDA_CMDF_DAT | SDA_CMDF_BUSY, errno);
+		return;
+	}
+
+	list_insert_tail(&slot->s_cmdlist, c);
+	sda_slot_exit(slot);
+
+	sda_slot_wakeup(slot);
+}
+
+void
+sda_cmd_resubmit_acmd(sda_slot_t *slot, sda_cmd_t *cmdp)
+{
+	sda_cmd_impl_t	*c = CIP(cmdp);
+
+	ASSERT(sda_slot_owned(slot));
+
+	c->c_index = c->c_acmd;
+	c->c_argument = c->c_aarg;
+	c->c_rtype = c->c_artype;
+	c->c_acmd = 0;
+
+	list_insert_head(&slot->s_cmdlist, c);
+}
+
+sda_cmd_t *
+sda_cmd_alloc(sda_slot_t *slot, sda_index_t index, uint32_t argument,
+    sda_rtype_t rtype, void *data, int kmflag)
+{
+	sda_cmd_impl_t	*c;
+
+	c = kmem_cache_alloc(sda_cmd_cache, kmflag);
+	if (c == NULL) {
+		return (NULL);
+	}
+	c->c_index = index;
+	c->c_rtype = rtype;
+	c->c_argument = argument;
+	c->c_resid = 0;
+	c->c_nblks = 0;
+	c->c_blksz = 0;
+
+	c->c_kvaddr = 0;
+	c->c_ndmac = 0;
+	c->c_dmacs = NULL;
+	c->c_flags = 0;
+
+	c->c_slot = slot;
+	c->c_errno = SDA_EOK;
+	c->c_done = NULL;
+	c->c_private = data;
+	c->c_acmd = 0;
+
+	return (&(c->c_public));
+}
+
+sda_cmd_t *
+sda_cmd_alloc_acmd(sda_slot_t *slot, sda_index_t index, uint32_t argument,
+    sda_rtype_t rtype, void *data, int kmflag)
+{
+	sda_cmd_impl_t	*c;
+
+	c = kmem_cache_alloc(sda_cmd_cache, kmflag);
+	if (c == NULL) {
+		return (NULL);
+	}
+	c->c_index = CMD_APP_CMD;
+	c->c_argument = index == ACMD_SD_SEND_OCR ? 0 : slot->s_rca << 16;
+	c->c_rtype = R1;
+	c->c_acmd = index;
+	c->c_artype = rtype;
+	c->c_aarg = argument;
+	c->c_resid = 0;
+	c->c_nblks = 0;
+	c->c_blksz = 0;
+
+	c->c_kvaddr = 0;
+	c->c_ndmac = 0;
+	c->c_dmacs = NULL;
+	c->c_flags = 0;
+
+	c->c_slot = slot;
+	c->c_errno = SDA_EOK;
+	c->c_done = NULL;
+	c->c_private = data;
+
+	return (&(c->c_public));
+}
+
+void
+sda_cmd_free(sda_cmd_t *cmdp)
+{
+	kmem_cache_free(sda_cmd_cache, cmdp);
+}
+
+sda_err_t
+sda_cmd_exec(sda_slot_t *slot, sda_cmd_t *cmdp, uint32_t *resp)
+{
+	int		errno;
+
+	if ((cmdp->sc_rtype & Rb) || (cmdp->sc_nblks != 0)) {
+		cmdp->sc_flags |= SDA_CMDF_DAT;
+	}
+	sda_cmd_submit(slot, cmdp,  NULL);
+
+	sda_cmd_wait(cmdp);
+
+	if (resp != NULL) {
+		switch (cmdp->sc_rtype) {
+		case R0:
+			break;
+		case R2:
+			resp[0] = cmdp->sc_response[0];
+			resp[1] = cmdp->sc_response[1];
+			resp[2] = cmdp->sc_response[2];
+			resp[3] = cmdp->sc_response[3];
+			break;
+		default:
+			resp[0] = cmdp->sc_response[0];
+			break;
+		}
+	}
+
+	errno = CIP(cmdp)->c_errno;
+
+	return (errno);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/sda_host.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,237 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SD card host support.  This is the API that host drivers access.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/varargs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sdcard/sda.h>
+#include <sys/sdcard/sda_impl.h>
+
+/*
+ * Static Variables.
+ */
+
+static struct bus_ops sda_host_bus_ops = {
+	BUSO_REV,			/* busops_rev */
+	nullbusmap,			/* bus_map */
+	NULL,				/* bus_get_intrspec (OBSOLETE) */
+	NULL,				/* bus_add_intrspec (OBSOLETE) */
+	NULL,				/* bus_remove_intrspec (OBSOLETE) */
+	i_ddi_map_fault,		/* bus_map_fault */
+	ddi_dma_map,			/* bus_dma_map */
+	ddi_dma_allochdl,		/* bus_dma_allochdl */
+	ddi_dma_freehdl,		/* bus_dma_freehdl */
+	ddi_dma_bindhdl,		/* bus_dma_bindhdl */
+	ddi_dma_unbindhdl,		/* bus_dma_unbindhdl */
+	ddi_dma_flush,			/* bus_dma_flush */
+	ddi_dma_win,			/* bus_dma_win */
+	ddi_dma_mctl,			/* bus_dma_ctl */
+	sda_nexus_bus_ctl,		/* bus_ctl */
+	ddi_bus_prop_op,		/* bus_prop_op */
+	NULL,				/* bus_get_eventcookie */
+	NULL,				/* bus_add_eventcall */
+	NULL,				/* bus_remove_eventcall */
+	NULL,				/* bus_post_event */
+	NULL,				/* bus_intr_ctl (OBSOLETE) */
+	NULL, /* sda_nexus_bus_config, */		/* bus_config */
+	NULL, /* sda_nexus_bus_unconfig, */		/* bus_unconfig */
+	NULL,				/* bus_fm_init */
+	NULL,				/* bus_fm_fini */
+	NULL,				/* bus_fm_access_enter */
+	NULL,				/* bus_fm_access_exit */
+	NULL,				/* bus_power */
+	NULL,				/* bus_intr_op */
+};
+
+static struct cb_ops sda_host_cb_ops = {
+	sda_nexus_open,			/* cb_open */
+	sda_nexus_close,		/* cb_close */
+	nodev,				/* cb_strategy */
+	nodev,				/* cb_print */
+	nodev,				/* cb_dump */
+	nodev,				/* cb_read */
+	nodev,				/* cb_write */
+	sda_nexus_ioctl,		/* cb_ioctl */
+	nodev,				/* cb_devmap */
+	nodev,				/* cb_mmap */
+	nodev,				/* cb_segmap */
+	nochpoll,			/* cb_poll */
+	ddi_prop_op,			/* cb_prop_op */
+	NULL,				/* cb_str */
+	D_MP,				/* cb_flag */
+	CB_REV,				/* cb_rev */
+	nodev,				/* cb_aread */
+	nodev,				/* cb_awrite */
+};
+
+/*
+ * Implementation.
+ */
+
+void
+sda_host_init_ops(struct dev_ops *devops)
+{
+	devops->devo_getinfo = sda_nexus_getinfo;
+	devops->devo_cb_ops = &sda_host_cb_ops;
+	devops->devo_bus_ops = &sda_host_bus_ops;
+}
+
+void
+sda_host_fini_ops(struct dev_ops *devops)
+{
+	devops->devo_bus_ops = NULL;
+}
+
+sda_host_t *
+sda_host_alloc(dev_info_t *dip, int nslot, sda_ops_t *ops, ddi_dma_attr_t *dma)
+{
+	sda_host_t	*h;
+	int		i;
+
+	if (ops->so_version != SDA_OPS_VERSION) {
+		return (NULL);
+	}
+
+	h = kmem_zalloc(sizeof (*h), KM_SLEEP);
+	h->h_nslot = nslot;
+	h->h_slots = kmem_zalloc(sizeof (sda_slot_t) * nslot, KM_SLEEP);
+	h->h_dma = dma;
+	h->h_dip = dip;
+
+	/* initialize each slot */
+	for (i = 0; i < nslot; i++) {
+		sda_slot_t *slot = &h->h_slots[i];
+
+		slot->s_host = h;
+		slot->s_slot_num = i;
+		slot->s_ops = *ops;
+
+		sda_slot_init(slot);
+	}
+
+	return (h);
+}
+
+void
+sda_host_free(sda_host_t *h)
+{
+	int	i;
+
+	for (i = 0; i < h->h_nslot; i++) {
+		sda_slot_fini(&h->h_slots[i]);
+	}
+
+	kmem_free(h->h_slots, sizeof (sda_slot_t) * h->h_nslot);
+	kmem_free(h, sizeof (*h));
+}
+
+void
+sda_host_set_private(sda_host_t *h, int num, void *private)
+{
+	h->h_slots[num].s_prv = private;
+}
+
+int
+sda_host_attach(sda_host_t *h)
+{
+	int	i;
+
+	/*
+	 * Attach slots.
+	 */
+	for (i = 0; i < h->h_nslot; i++) {
+
+		sda_slot_attach(&h->h_slots[i]);
+
+		/*
+		 * Initiate card detection.
+		 */
+		sda_host_detect(h, i);
+	}
+
+	/*
+	 * Register (create) nexus minor nodes.
+	 */
+	sda_nexus_register(h);
+
+	return (DDI_SUCCESS);
+}
+
+void
+sda_host_detach(sda_host_t *h)
+{
+	int	i;
+
+	/*
+	 * Unregister nexus minor nodes.
+	 */
+	sda_nexus_unregister(h);
+
+	/*
+	 * Detach slots.
+	 */
+	for (i = 0; i < h->h_nslot; i++) {
+		sda_slot_detach(&h->h_slots[i]);
+	}
+}
+
+void
+sda_host_transfer(sda_host_t *h, int num, sda_err_t errno)
+{
+	sda_slot_transfer(&h->h_slots[num], errno);
+}
+
+void
+sda_host_detect(sda_host_t *h, int num)
+{
+	sda_slot_detect(&h->h_slots[num]);
+}
+
+void
+sda_host_fault(sda_host_t *h, int num, sda_fault_t fail)
+{
+	sda_slot_fault(&h->h_slots[num], fail);
+}
+
+void
+sda_host_log(sda_host_t *h, int snum, const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	if (h != NULL) {
+		sda_slot_log(&h->h_slots[snum], fmt, ap);
+	} else {
+		sda_slot_log(NULL, fmt, ap);
+	}
+	va_end(ap);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/sda_init.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,654 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SD card initialization support.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sdcard/sda.h>
+#include <sys/sdcard/sda_impl.h>
+
+
+/*
+ * Local Prototypes.
+ */
+
+static sda_err_t sda_init_mmc(sda_slot_t *);
+static sda_err_t sda_init_sdio(sda_slot_t *);
+static sda_err_t sda_init_sdmem(sda_slot_t *);
+static sda_err_t sda_init_cmd(sda_slot_t *, sda_index_t, uint32_t,
+    sda_rtype_t, uint32_t *);
+static sda_err_t sda_init_acmd(sda_slot_t *, sda_index_t, uint32_t,
+    sda_rtype_t, uint32_t *);
+static sda_err_t sda_init_blocklen(sda_slot_t *);
+static sda_err_t sda_init_width(sda_slot_t *);
+static sda_err_t sda_init_rca(sda_slot_t *);
+static sda_err_t sda_init_ifcond(sda_slot_t *);
+static sda_err_t sda_init_highspeed(sda_slot_t *);
+static sda_err_t sda_init_switch(sda_slot_t *, uint8_t, uint8_t, uint8_t,
+    uint8_t *);
+static void sda_init_clock(sda_slot_t *, uint32_t);
+
+/*
+ * Implementation.
+ */
+sda_err_t
+sda_init_cmd(sda_slot_t *slot, sda_index_t cmd, uint32_t arg,
+    sda_rtype_t rtype, uint32_t *resp)
+{
+	sda_cmd_t	*cmdp;
+	sda_err_t	errno;
+
+	cmdp = sda_cmd_alloc(slot, cmd, arg, rtype, NULL, KM_SLEEP);
+
+	cmdp->sc_flags |= SDA_CMDF_INIT;
+
+	errno = sda_cmd_exec(slot, cmdp, resp);
+
+	sda_cmd_free(cmdp);
+
+	return (errno);
+}
+
+sda_err_t
+sda_init_acmd(sda_slot_t *slot, sda_index_t cmd, uint32_t arg,
+    sda_rtype_t rtype, uint32_t *resp)
+{
+	sda_cmd_t	*cmdp;
+	sda_err_t	errno;
+
+	cmdp = sda_cmd_alloc_acmd(slot, cmd, arg, rtype, NULL, KM_SLEEP);
+
+	cmdp->sc_flags |= SDA_CMDF_INIT;
+
+	errno = sda_cmd_exec(slot, cmdp, resp);
+
+	sda_cmd_free(cmdp);
+
+	return (errno);
+}
+
+sda_err_t
+sda_init_sdio(sda_slot_t *slot)
+{
+	slot->s_num_io = 0;
+
+	/*
+	 * TODO: SDIO: We need to initialize the SDIO OCR register using
+	 * the special CMD_IO_SEND_OCR (CMD5) command.
+	 */
+	return (SDA_EOK);
+}
+
+sda_err_t
+sda_init_sdmem(sda_slot_t *slot)
+{
+	uint32_t	ocr;
+	int		count;
+
+	slot->s_flags &= ~SLOTF_SDMEM;
+
+	/*
+	 * Try sending the ACMD41 to query the OCR (Op Cond Register).
+	 */
+	if (sda_init_acmd(slot, ACMD_SD_SEND_OCR, 0, R3, &ocr) != SDA_EOK) {
+		/*
+		 * Card failed to respond to query, not an SD card?
+		 * We send GO_IDLE to clear any error status on the
+		 * card.
+		 */
+		(void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
+		return (SDA_EOK);
+	}
+
+	/*
+	 * Now we have to send our OCR value, along with the HCS (High
+	 * Capacity Support) bit.  The HCS bit is required, to
+	 * activate high capacity cards.  We only set the HCS bit if
+	 * the card responded to CMD8 (SEND_IFCOND), indicating that
+	 * it supports the new protocol.
+	 *
+	 * Note that the HCS bit occupies the same location as the CCS bit
+	 * in the response.
+	 */
+	if ((ocr & slot->s_cur_ocr) == 0) {
+		sda_slot_err(slot, "SD card not compatible with host");
+		return (SDA_ENOTSUP);
+	}
+	/* set the HCS bit if its a ver 2.00 card */
+	if (slot->s_flags & SLOTF_IFCOND) {
+		ocr |= OCR_CCS;
+	}
+
+	/* make sure card is powered up */
+	for (count = 1000000; count != 0; count -= 10000) {
+		uint32_t	r3;
+
+		if (sda_init_acmd(slot, ACMD_SD_SEND_OCR, ocr, R3, &r3) != 0) {
+			sda_slot_err(slot, "SD card failed to power up");
+			return (SDA_ENOTSUP);
+		}
+
+		/* Now check the busy bit */
+		if (r3 & OCR_POWER_UP) {
+			slot->s_flags |= SLOTF_SDMEM;
+			if ((slot->s_flags & SLOTF_IFCOND) &&
+			    (r3 & OCR_CCS)) {
+				slot->s_flags |= SLOTF_SDHC;
+			} else {
+				slot->s_flags &= ~SLOTF_SDHC;
+			}
+			return (0);
+		}
+
+		drv_usecwait(10000);
+	}
+
+	sda_slot_err(slot, "SD card timed out during power up");
+	return (SDA_ETIME);
+}
+
+sda_err_t
+sda_init_mmc(sda_slot_t *slot)
+{
+	uint32_t	ocr;
+	int		count;
+
+	slot->s_flags &= ~SLOTF_MMC;
+
+	/*
+	 * If the card has already been identified as an SD card, then
+	 * cannot also be an MMC card, so don't probe it as such.
+	 */
+	if (slot->s_flags & SLOTF_SD) {
+		return (SDA_EOK);
+	}
+
+	/*
+	 * Try sending the CMD1 to query the OCR.
+	 */
+	if (sda_init_cmd(slot, CMD_SEND_OCR, 0, R3, &ocr) != 0) {
+		/*
+		 * Card failed to respond to query, not an MMC card?
+		 * We send GO_IDLE to clear any error status on the
+		 * card.
+		 */
+		(void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
+		return (SDA_EOK);
+	}
+
+	if ((ocr & slot->s_cur_ocr) == 0) {
+		sda_slot_err(slot, "MMC card not compatible with host");
+		return (SDA_ENOTSUP);
+	}
+
+	/* make sure card is powered up */
+	for (count = 1000000; count != 0; count -= 10000) {
+		uint32_t	r3;
+
+		if (sda_init_cmd(slot, CMD_SEND_OCR, ocr, R3, &r3) != 0) {
+			sda_slot_err(slot, "MMC card failed to power up");
+			return (SDA_ENOTSUP);
+		}
+
+		/* Now check the busy bit */
+		if (r3 & OCR_POWER_UP) {
+			slot->s_flags |= SLOTF_MMC;
+			return (SDA_EOK);
+		}
+
+		drv_usecwait(10000);
+	}
+
+	sda_slot_err(slot, "MMC card timed out during power up");
+	return (SDA_ETIME);
+}
+
+sda_err_t
+sda_init_card(sda_slot_t *slot)
+{
+	int		rv;
+	uint32_t	resp;
+	uint32_t	val;
+
+	/*
+	 * Power off slot/card initially.
+	 */
+	sda_slot_power_off(slot);
+
+	/*
+	 * Apply initial power to the slot.
+	 */
+	if ((rv = sda_slot_power_on(slot)) != 0) {
+		return (rv);
+	}
+
+	/*
+	 * First enable the clock, but only at 400 kHz.  All cards are
+	 * supposed to be able to operate between this speed and 100
+	 * kHz, and all hosts must be able to pick a speed between 100
+	 * kHz and 400 kHz.
+	 *
+	 * Once we know what the device can support, then we speed up.
+	 */
+	sda_init_clock(slot, 400000);
+
+	if ((rv = sda_init_ifcond(slot)) != SDA_EOK) {
+		goto done;
+	}
+
+	if (((rv = sda_init_sdio(slot)) != SDA_EOK) ||
+	    ((rv = sda_init_sdmem(slot)) != SDA_EOK) ||
+	    ((rv = sda_init_mmc(slot)) != SDA_EOK)) {
+
+		/* message will already have been logged */
+		goto done;
+	}
+
+	if ((slot->s_flags & (SLOTF_MEMORY | SLOTF_SDIO)) == 0) {
+		sda_slot_err(slot, "Unidentified card type");
+		rv = SDA_ENOTSUP;
+		goto done;
+	}
+
+	/*
+	 * Memory cards need to obtain their CID before getting their RCA.
+	 * This is a requirement for the state transitions... they go thru
+	 * the ident state, unlike SDIO cards.
+	 */
+	if (slot->s_flags & SLOTF_MEMORY) {
+		rv = sda_init_cmd(slot, CMD_BCAST_CID, 0, R2, slot->s_rcid);
+		if (rv != SDA_EOK) {
+			sda_slot_err(slot, "Failed getting card CID (%d)", rv);
+			goto done;
+		}
+	}
+
+	if ((rv = sda_init_rca(slot)) != SDA_EOK) {
+		goto done;
+	}
+
+	slot->s_maxclk = 0xffffffffU;	/* special sentinel */
+
+	/*
+	 * Figure out card supported bus width and speed.
+	 *
+	 * TODO: SDIO: For IO cards, we have to check what speed the card
+	 * supports by looking in the CCCR_CAPAB register.  (SDIO cards
+	 * can go low-speed only, full-speed, or high-speed.)
+	 */
+	if (slot->s_flags & SLOTF_MEMORY) {
+
+		/*
+		 * We need to obtain the CSD.
+		 */
+		rv = sda_init_cmd(slot, CMD_SEND_CSD, slot->s_rca << 16, R2,
+		    slot->s_rcsd);
+		if (rv != 0) {
+			sda_slot_err(slot, "Failed getting card CSD (%d)", rv);
+			goto done;
+		}
+
+		/*
+		 * Calculate the maxclock.
+		 */
+		slot->s_maxclk = sda_mem_maxclk(slot);
+	}
+	if (((slot->s_flags & SLOTF_SDMEM) != 0) &&
+	    ((slot->s_caps & SLOT_CAP_4BITS) != 0)) {
+		slot->s_flags |= SLOTF_4BITS;
+	}
+	if (slot->s_flags & SLOTF_SDIO) {
+		sda_slot_debug(slot, "Wide SDIO bus not yet supported");
+		slot->s_flags &= ~SLOTF_4BITS;
+	}
+
+	/*
+	 * Now select the card.
+	 */
+	if ((rv = sda_init_cmd(slot, CMD_SELECT_CARD, slot->s_rca << 16,
+	    R1b, &resp)) != SDA_EOK) {
+		sda_slot_err(slot, "Failed selecting card (%d, %x)", rv, resp);
+		goto done;
+	}
+
+	if ((rv = sda_init_highspeed(slot)) != SDA_EOK) {
+		goto done;
+	}
+
+	sda_init_clock(slot, slot->s_maxclk);
+
+	/*
+	 * Lets go to 4-bit bus mode, if possible.
+	 */
+	if ((rv = sda_init_width(slot)) != SDA_EOK) {
+		goto done;
+	}
+
+	if ((rv = sda_init_blocklen(slot)) != SDA_EOK) {
+		goto done;
+	}
+
+	/* note if a card is writable */
+	if ((sda_getprop(slot, SDA_PROP_WPROTECT, &val) == SDA_EOK) &&
+	    (val == 0)) {
+		slot->s_flags |= SLOTF_WRITABLE;
+	}
+
+	rv = SDA_EOK;
+
+done:
+
+	sda_slot_enter(slot);
+	slot->s_init = B_FALSE;
+	sda_slot_exit(slot);
+
+	sda_slot_wakeup(slot);
+
+	return (rv);
+}
+
+sda_err_t
+sda_init_blocklen(sda_slot_t *slot)
+{
+	int		rv;
+	uint32_t	resp;
+
+	if ((slot->s_flags & SLOTF_MEMORY) == 0)  {
+		return (SDA_EOK);
+	}
+
+	/*
+	 * All memory cards support block sizes of 512.  Full stop.
+	 */
+	rv = sda_init_cmd(slot, CMD_SET_BLOCKLEN, 512, R1, &resp);
+	if (rv != SDA_EOK) {
+		sda_slot_err(slot, "Unable to set block length (%d, %x)",
+		    rv, resp);
+	}
+	return (rv);
+}
+
+void
+sda_init_clock(sda_slot_t *slot, uint32_t hz)
+{
+	int		rv;
+	uint32_t	act;
+
+	/*
+	 * Note that at no time is a failure programming the clock
+	 * itself necessarily a fatal error.  Although if the clock
+	 * wasn't programmed, other things will probably not work during
+	 * initialization.
+	 */
+
+	if ((rv = sda_setprop(slot, SDA_PROP_CLOCK, hz)) != SDA_EOK) {
+		sda_slot_err(slot, "Failed setting clock to %u Hz (%d)",
+		    hz, rv);
+		/* XXX: FMA fail the slot */
+		return;
+	}
+
+	if ((rv = sda_getprop(slot, SDA_PROP_CLOCK, &act)) == SDA_EOK) {
+		sda_slot_debug(slot, "Clock set to %u Hz (requested %u Hz)",
+		    act, hz);
+	} else {
+		sda_slot_debug(slot, "Clock frequency unknown (good luck).");
+	}
+
+	/*
+	 * For now, just wait 10msec for clocks to stabilize to the
+	 * card.  (Is this really necessary?)
+	 */
+	delay(drv_usectohz(10000));
+}
+
+sda_err_t
+sda_init_width(sda_slot_t *slot)
+{
+	int		rv;
+	uint32_t	resp;
+
+	/*
+	 * Spec says we should command the card first.
+	 */
+
+	rv = sda_setprop(slot, SDA_PROP_BUSWIDTH, 1);
+	if (rv != SDA_EOK) {
+		sda_slot_err(slot, "Unable to set slot 1-bit mode (%d)", rv);
+		return (rv);
+	}
+
+	if ((slot->s_flags & SLOTF_4BITS) == 0) {
+		return (SDA_EOK);
+	}
+
+	/*
+	 * TODO: SDIO: SDIO cards set the CCCR_BUS_WIDTH
+	 * and CCCR_CD_DISABLE bits here.
+	 */
+
+	/*
+	 * If we're going to use all 4 pins, we really need to disconnect
+	 * the card pullup resistor.   A consquence of this, is that hosts
+	 * which use that resistor for detection must not claim to support
+	 * 4-bit bus mode.  This is a limitation of our implementation.
+	 */
+	rv = sda_init_acmd(slot, ACMD_SET_CLR_CARD_DETECT, 1, R1, &resp);
+	if (rv != SDA_EOK) {
+		sda_slot_err(slot,
+		    "Unable disconnect DAT3 resistor on card (%d, %x)",
+		    rv, resp);
+		/* non-fatal error, muddle on */
+		return (SDA_EOK);
+	}
+
+	rv = sda_init_acmd(slot, ACMD_SET_BUS_WIDTH, 2, R1, &resp);
+	if (rv != SDA_EOK) {
+		sda_slot_err(slot, "Unable to set card 4-bit mode (%d, %x)",
+		    rv, resp);
+		/* non-fatal error, muddle on */
+		return (SDA_EOK);
+	}
+
+	rv = sda_setprop(slot, SDA_PROP_BUSWIDTH, 4);
+	if (rv != SDA_EOK) {
+		/*
+		 * This is bad news.  We've already asked for the card to
+		 * to use 4-bit mode, but the host is not complying.  It
+		 * shouldn't ever happen, so we just error out.
+		 */
+		sda_slot_err(slot, "Unable to set slot 4-bit mode (%d)", rv);
+	}
+
+	return (rv);
+}
+
+sda_err_t
+sda_init_ifcond(sda_slot_t *slot)
+{
+	int		rv;
+	int		tries;
+	uint32_t	vchk;
+	uint32_t	resp;
+
+	/*
+	 * Try SEND_IF_COND.  Note that this assumes that the host is
+	 * supplying 2.7 - 3.6 voltage range.  The standard is not
+	 * defined for any other ranges.
+	 */
+	vchk = R7_VHS_27_36V | R7_PATTERN;
+
+	/* we try this a few times, just to be sure */
+	for (tries = 0; tries < 5; tries++) {
+		rv = sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
+		if (rv != SDA_EOK) {
+			sda_slot_err(slot, "Failed to IDLE card");
+			return (rv);
+		}
+
+		rv = sda_init_cmd(slot, CMD_SEND_IF_COND, vchk, R7, &resp);
+		if (rv == SDA_EOK) {
+			break;
+		}
+		delay(drv_usectohz(10000));
+	}
+
+	if (rv != SDA_EOK) {
+		(void) sda_init_cmd(slot, CMD_GO_IDLE, 0, R0, NULL);
+		slot->s_flags &= ~SLOTF_IFCOND;
+
+	} else if (resp != vchk) {
+		sda_slot_err(slot, "Card voltages incompatible! (%x)", resp);
+		return (SDA_ENOTSUP);
+
+	} else {
+		/* SDHC compliant */
+		slot->s_flags |= SLOTF_IFCOND;
+	}
+
+	return (SDA_EOK);
+}
+
+sda_err_t
+sda_init_rca(sda_slot_t *slot)
+{
+	int		rv;
+	int		tries;
+	uint32_t	resp;
+
+	/*
+	 * Program the RCA.  Note that MMC has a different mechanism
+	 * for this.
+	 */
+	for (tries = 0; tries < 10; tries++) {
+
+		if (slot->s_flags & SLOTF_MMC) {
+			/*
+			 * For MMC, we push the RCA to the MMC.  We
+			 * arbitrarily start at 0x100, and add from
+			 * there.
+			 */
+			rv = sda_init_cmd(slot, CMD_SEND_RCA,
+			    (0x100 + tries) << 16, R1, NULL);
+			if (rv == SDA_EOK)
+				slot->s_rca = 0x100 + tries;
+		} else {
+			/*
+			 * For SDcard, we are basically asking the
+			 * card to propose a value.  It *may* propose
+			 * a value of zero, in which case we will have
+			 * to ask again.
+			 */
+			rv = sda_init_cmd(slot, CMD_SEND_RCA, 0, R6, &resp);
+			if (rv == SDA_EOK)
+				slot->s_rca = resp >> 16;
+		}
+		if ((rv == SDA_EOK) && (slot->s_rca != 0)) {
+			sda_slot_debug(slot, "Relative address (RCA) = %d",
+			    slot->s_rca);
+			return (SDA_EOK);
+		}
+	}
+
+	sda_slot_err(slot, "Unable to negotiate a suitable RCA (%d)", rv);
+	return ((rv != SDA_EOK) ? rv : SDA_EINVAL);
+}
+
+sda_err_t
+sda_init_switch(sda_slot_t *slot, uint8_t mode, uint8_t grp, uint8_t val,
+    uint8_t *data)
+{
+	sda_cmd_t	*cmdp;
+	sda_err_t	errno;
+	uint32_t	arg;
+
+	/*
+	 * The spec says we should leave unselected groups set to 0xf,
+	 * to prevent inadvertent changes.
+	 */
+	arg = (mode << 31) | 0xffffff;
+	arg &= ~(0xf << (grp << 2));
+	arg |= (val << (grp << 2));
+
+	cmdp = sda_cmd_alloc(slot, CMD_SWITCH_FUNC, arg, R1, NULL, KM_SLEEP);
+
+	cmdp->sc_flags |= SDA_CMDF_INIT | SDA_CMDF_DAT | SDA_CMDF_READ;
+	cmdp->sc_blksz = 64;
+	cmdp->sc_nblks = 1;
+	cmdp->sc_kvaddr = (void *)data;
+
+	errno = sda_cmd_exec(slot, cmdp, NULL);
+
+	sda_cmd_free(cmdp);
+
+	return (errno);
+
+}
+
+sda_err_t
+sda_init_highspeed(sda_slot_t *slot)
+{
+	uint32_t	ccc;
+	uint8_t		data[64];
+	sda_err_t	rv;
+
+	if ((slot->s_caps & SLOT_CAP_HISPEED) == 0) {
+		return (SDA_EOK);
+	}
+	if ((slot->s_flags & SLOTF_SDMEM) == 0) {
+		return (SDA_EOK);
+	}
+	ccc = sda_mem_getbits(slot->s_rcsd, 95, 12);
+	if ((ccc & (1 << 10)) == 0) {
+		return (SDA_EOK);
+	}
+
+	rv = sda_init_switch(slot, 0, 0, 1, data);
+
+	/* these are big-endian bits, bit 401 */
+	if ((rv != SDA_EOK) || ((data[13] & (1 << 1)) == 0)) {
+		return (SDA_EOK);
+	}
+
+	rv = sda_init_switch(slot, 1, 0, 1, data);
+	if (rv != SDA_EOK) {
+		return (SDA_EOK);
+	}
+
+	/* now program the card */
+	rv = sda_setprop(slot, SDA_PROP_HISPEED, 1);
+	if (rv != SDA_EOK) {
+		sda_slot_err(slot, "Failed setting slot to high speed mode");
+	} else {
+		/* the card should now support 50 MHz */
+		slot->s_maxclk = 50000000;
+	}
+
+	return (rv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/sda_mem.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,653 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Memory target support for SDcard.
+ */
+
+#include <sys/types.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/scsi/adapters/blk2scsa.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sdcard/sda.h>
+#include <sys/sdcard/sda_impl.h>
+
+static int sda_mem_attach(dev_info_t *, ddi_attach_cmd_t);
+static int sda_mem_detach(dev_info_t *, ddi_detach_cmd_t);
+static b2s_err_t sda_mem_b2s_errno(sda_err_t);
+static boolean_t sda_mem_b2s_request(void *, b2s_request_t *);
+static boolean_t sda_mem_b2s_rw(sda_slot_t *, b2s_request_t *);
+static void sda_mem_b2s_done(sda_cmd_t *);
+static void sda_mem_getstring(uint32_t *, char *, int, int);
+static int sda_mem_parse_cid_csd(sda_slot_t *, dev_info_t *);
+static int sda_mem_cmd(sda_slot_t *, uint8_t, uint32_t, uint8_t, uint32_t *);
+
+
+/*
+ * To minimize complexity and reduce layering, we implement almost the
+ * entire memory card driver (sdcard) here.  The memory card still
+ * needs to be a separate driver though, due to the requirement to
+ * have both SCSI HBA bus ops and SD bus ops.
+ */
+
+/*
+ * SCSA layer supplies a cb_ops, but we don't want it, because we
+ * don't want to expose a SCSI attachment point.  (Our parent handles
+ * the attachment point, the SCSI one would be confusing.)  We have to
+ * supply a stubbed out one, to prevent SCSA from trying to create minor
+ * nodes on our behalf.
+ *
+ * Perhaps at some future point we might want to expose a separate set
+ * of ioctls for these nodes, but for now we rely on our parent to do
+ * all that work.
+ */
+static struct cb_ops sda_mem_ops = {
+	nodev,			/* cb_open */
+	nodev,			/* cb_close */
+	nodev,			/* cb_strategy */
+	nodev,			/* cb_print */
+	nodev,			/* cb_dump */
+	nodev,			/* cb_read */
+	nodev,			/* cb_write */
+	nodev,			/* cb_ioctl */
+	nodev,			/* cb_devmap */
+	nodev,			/* cb_mmap */
+	nodev,			/* cb_segmap */
+	nochpoll,		/* cb_chpoll */
+	ddi_prop_op,		/* cb_prop_op */
+	NULL,			/* cb_stream */
+	D_MP			/* cb_flag */
+};
+
+/*
+ * Here are the public functions.
+ */
+void
+sda_mem_init(struct modlinkage *modlp)
+{
+	struct dev_ops *devo;
+
+	devo = ((struct modldrv *)(modlp->ml_linkage[0]))->drv_dev_ops;
+	devo->devo_attach = sda_mem_attach;
+	devo->devo_detach = sda_mem_detach;
+
+	devo->devo_cb_ops = &sda_mem_ops;
+
+	/* it turns out that this can't ever really fail */
+	(void) b2s_mod_init(modlp);
+}
+
+void
+sda_mem_fini(struct modlinkage *modlp)
+{
+	b2s_mod_fini(modlp);
+}
+
+/*
+ * Everything beyond this is private.
+ */
+
+int
+sda_mem_cmd(sda_slot_t *slot, uint8_t cmd, uint32_t arg, uint8_t rtype,
+    uint32_t *resp)
+{
+	sda_cmd_t	*cmdp;
+	int		errno;
+
+	cmdp = sda_cmd_alloc(slot, cmd, arg, rtype, NULL, KM_SLEEP);
+	if (cmdp == NULL) {
+		return (ENOMEM);
+	}
+	errno = sda_cmd_exec(slot, cmdp, resp);
+	sda_cmd_free(cmdp);
+
+	return (errno);
+}
+
+boolean_t
+sda_mem_b2s_rw(sda_slot_t *slot, b2s_request_t *reqp)
+{
+	sda_cmd_t	*cmdp;
+	uint64_t	nblks;
+	uint64_t	blkno;
+	uint16_t	rblen;
+	int		rv;
+	uint8_t		index;
+	uint16_t	flags;
+
+	blkno = reqp->br_lba;
+	nblks = reqp->br_nblks;
+
+	switch (reqp->br_cmd) {
+	case B2S_CMD_READ:
+		if (nblks > 1) {
+			index = CMD_READ_MULTI;
+			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ |
+			    SDA_CMDF_AUTO_CMD12;
+		} else {
+			index = CMD_READ_SINGLE;
+			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ;
+		}
+		break;
+	case B2S_CMD_WRITE:
+		if (nblks > 1) {
+			index = CMD_WRITE_MULTI;
+			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE |
+			    SDA_CMDF_AUTO_CMD12;
+		} else {
+			index = CMD_WRITE_SINGLE;
+			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE;
+		}
+		break;
+	default:
+		ASSERT(0);
+		break;
+	}
+
+	cmdp = sda_cmd_alloc(slot, index, blkno << slot->s_bshift,
+	    R1, reqp, KM_NOSLEEP);
+	if (cmdp == NULL) {
+		b2s_request_done(reqp, B2S_ENOMEM, 0);
+		return (B_TRUE);
+	}
+
+	if (slot->s_host->h_dma != NULL) {
+		b2s_request_dma(reqp, &cmdp->sc_ndmac, &cmdp->sc_dmacs);
+		cmdp->sc_kvaddr = 0;
+	}
+	if ((slot->s_caps & SLOT_CAP_NOPIO) == 0) {
+		size_t	maplen;
+		b2s_request_mapin(reqp, &cmdp->sc_kvaddr, &maplen);
+		cmdp->sc_ndmac = 0;
+	}
+
+	if (nblks == 0) {
+		/*
+		 * This is not strictly a failure, but no work to do.
+		 * We have to do it late here because we don't want to
+		 * by pass the above media readiness checks.
+		 */
+		rv = B2S_EOK;
+		goto failed;
+	}
+	if (nblks > 0xffff) {
+		rv = B2S_EINVAL;
+		goto failed;
+	}
+
+	rblen = slot->s_blksz;
+
+	if ((blkno + nblks) > slot->s_nblks) {
+		rv = B2S_EBLKADDR;
+		goto failed;
+	}
+
+	cmdp->sc_rtype = R1;
+	cmdp->sc_blksz = rblen;
+	cmdp->sc_nblks = (uint16_t)nblks;
+	cmdp->sc_index = index;
+	cmdp->sc_flags = flags;
+
+	sda_cmd_submit(slot, cmdp, sda_mem_b2s_done);
+	return (B_TRUE);
+
+failed:
+	sda_cmd_free(cmdp);
+	b2s_request_done(reqp, rv, 0);
+	return (B_TRUE);
+}
+
+boolean_t
+sda_mem_b2s_format(sda_slot_t *slot, b2s_request_t *reqp)
+{
+	sda_cmd_t	*cmdp;
+	int		rv;
+
+
+	rv = sda_mem_cmd(slot, CMD_ERASE_START, 0, R1, NULL);
+	if (rv != 0) {
+		b2s_request_done(reqp, sda_mem_b2s_errno(rv), 0);
+		return (B_TRUE);
+	}
+	rv = sda_mem_cmd(slot, CMD_ERASE_END, slot->s_nblks - 1, R1, NULL);
+	if (rv != 0) {
+		b2s_request_done(reqp, sda_mem_b2s_errno(rv), 0);
+		return (B_TRUE);
+	}
+
+	cmdp = sda_cmd_alloc(slot, CMD_ERASE, 0, R1b, reqp, KM_NOSLEEP);
+	if (cmdp == NULL) {
+		b2s_request_done(reqp, B2S_ENOMEM, 0);
+		return (B_TRUE);
+	}
+	cmdp->sc_flags = SDA_CMDF_DAT | SDA_CMDF_MEM;
+
+	sda_cmd_submit(slot, cmdp, sda_mem_b2s_done);
+	return (B_TRUE);
+}
+
+b2s_err_t
+sda_mem_b2s_errno(sda_err_t errno)
+{
+	/* the hot path */
+	if (errno == SDA_EOK) {
+		return (B2S_EOK);
+	}
+
+	switch (errno) {
+	case SDA_ENOMEM:
+		return (B2S_ENOMEM);
+	case SDA_ETIME:
+		return (B2S_ETIMEDOUT);
+	case SDA_EWPROTECT:
+		return (B2S_EWPROTECT);
+	case SDA_ESUSPENDED:
+	case SDA_ENODEV:
+		return (B2S_ENOMEDIA);
+	case SDA_EFAULT:
+	case SDA_ECRC7:
+	case SDA_EPROTO:
+		return (B2S_EHARDWARE);
+	case SDA_ERESET:
+		return (B2S_ERESET);
+	case SDA_EIO:
+	case SDA_ERESID:
+	default:
+		return (B2S_EIO);
+	}
+}
+
+void
+sda_mem_b2s_done(sda_cmd_t *cmdp)
+{
+	b2s_request_t	*reqp = sda_cmd_data(cmdp);
+	int		errno = sda_cmd_errno(cmdp);
+
+	b2s_request_done(reqp, sda_mem_b2s_errno(errno), cmdp->sc_resid);
+	sda_cmd_free(cmdp);
+}
+
+boolean_t
+sda_mem_b2s_request(void *arg, b2s_request_t *reqp)
+{
+	sda_slot_t	*slot = arg;
+	int		rv;
+
+	switch (reqp->br_cmd) {
+	case B2S_CMD_WRITE:
+		if ((slot->s_flags & SLOTF_WRITABLE) == 0) {
+			rv = B2S_EWPROTECT;
+		} else {
+			return (sda_mem_b2s_rw(slot, reqp));
+		}
+		break;
+
+	case B2S_CMD_READ:
+		return (sda_mem_b2s_rw(slot, reqp));
+
+	case B2S_CMD_INQUIRY:
+		reqp->br_inquiry.inq_vendor = "OSOL";
+		reqp->br_inquiry.inq_product =
+		    slot->s_flags & SLOTF_MMC ? "MultiMediaCard" :
+		    slot->s_flags & SLOTF_SDHC ? "SDHC Memory Card" :
+		    "SD Memory Card";
+		reqp->br_inquiry.inq_revision = "";
+		reqp->br_inquiry.inq_serial = "";
+		rv = B2S_EOK;
+		break;
+
+	case B2S_CMD_GETMEDIA:
+		if (!slot->s_ready) {
+			rv = B2S_ENODEV;
+		} else {
+			reqp->br_media.media_blksz = slot->s_blksz;
+			reqp->br_media.media_nblks = slot->s_nblks;
+			/* detect read-only cards */
+			if (slot->s_flags & SLOTF_WRITABLE) {
+				reqp->br_media.media_flags = 0;
+			} else {
+				reqp->br_media.media_flags =
+				    B2S_MEDIA_FLAG_READ_ONLY;
+			}
+			rv = B2S_EOK;
+		}
+		break;
+
+	case B2S_CMD_FORMAT:
+		return (sda_mem_b2s_format(slot, reqp));
+
+	case B2S_CMD_ABORT:
+		sda_slot_mem_reset(slot, SDA_EABORT);
+		rv = B2S_EOK;
+		break;
+
+	case B2S_CMD_RESET:
+		sda_slot_mem_reset(slot, SDA_ERESET);
+		rv = B2S_EOK;
+		break;
+
+	case B2S_CMD_START:
+	case B2S_CMD_STOP:
+	case B2S_CMD_SYNC:
+		rv = B2S_EOK;
+		break;
+
+	case B2S_CMD_LOCK:
+	case B2S_CMD_UNLOCK:
+	default:
+		rv = B2S_ENOTSUP;
+		break;
+	}
+
+	b2s_request_done(reqp, rv, 0);
+	return (B_TRUE);
+}
+
+int
+sda_mem_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	sda_slot_t		*slot;
+	b2s_nexus_t		*nexus;
+	b2s_nexus_info_t	nexinfo;
+	b2s_leaf_info_t		leafinfo;
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		if ((slot = ddi_get_parent_data(dip)) == NULL) {
+			return (DDI_FAILURE);
+		}
+
+		if (sda_mem_parse_cid_csd(slot, dip) != DDI_SUCCESS) {
+			return (DDI_FAILURE);
+		}
+
+		nexinfo.nexus_version = B2S_VERSION_0;
+		nexinfo.nexus_private = slot;
+		nexinfo.nexus_dip = dip;
+		nexinfo.nexus_dma_attr = slot->s_host->h_dma;
+		nexinfo.nexus_request = sda_mem_b2s_request;
+
+		nexus = b2s_alloc_nexus(&nexinfo);
+		if (nexus == NULL) {
+			return (DDI_FAILURE);
+		}
+
+		leafinfo.leaf_target = 0;
+		leafinfo.leaf_lun = 0;
+		leafinfo.leaf_flags =
+		    B2S_LEAF_REMOVABLE | B2S_LEAF_HOTPLUGGABLE;
+		leafinfo.leaf_unique_id = slot->s_uuid;
+
+		slot->s_leaf = b2s_attach_leaf(nexus, &leafinfo);
+		if (slot->s_leaf == NULL) {
+			b2s_free_nexus(nexus);
+			return (DDI_FAILURE);
+		}
+
+		slot->s_nexus = nexus;
+		if (b2s_attach_nexus(nexus) != DDI_SUCCESS) {
+			slot->s_nexus = NULL;
+			b2s_free_nexus(nexus);
+			return (DDI_FAILURE);
+		}
+		slot->s_nexus = nexus;
+
+		return (DDI_SUCCESS);
+
+
+	case DDI_RESUME:
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+int
+sda_mem_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	sda_slot_t	*slot;
+	b2s_nexus_t	*nexus;
+
+	switch (cmd) {
+	case DDI_DETACH:
+		if ((slot = ddi_get_parent_data(dip)) == NULL) {
+			return (DDI_FAILURE);
+		}
+		if ((nexus = slot->s_nexus) == NULL) {
+			/* nothing to do */
+			return (DDI_SUCCESS);
+		}
+		if (b2s_detach_nexus(nexus) != DDI_SUCCESS) {
+			return (DDI_FAILURE);
+		}
+		slot->s_nexus = NULL;
+		b2s_free_nexus(nexus);
+		return (DDI_SUCCESS);
+
+	case DDI_SUSPEND:
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+uint32_t
+sda_mem_getbits(uint32_t *resp, int hibit, int len)
+{
+	uint32_t	val = 0;
+	uint32_t	bit;
+
+	for (bit = hibit; len--; bit--) {
+		val <<= 1;
+		val |= ((resp[bit / 32]) >> (bit % 32)) & 1;
+	}
+	return (val);
+}
+
+void
+sda_mem_getstring(uint32_t *resp, char *s, int hibit, int len)
+{
+	while (len--) {
+		*s++ = sda_mem_getbits(resp, hibit, 8);
+		hibit -= 8;
+	}
+	*s = 0;
+}
+
+uint32_t
+sda_mem_maxclk(sda_slot_t *slot)
+{
+	static const uint32_t	mult[16] = {
+		0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
+	};
+
+	static const uint32_t	units[8] = {
+		10000, 100000, 1000000, 10000000, 0, 0, 0, 0,
+	};
+	uint8_t			ts;
+
+	ts = sda_mem_getbits(slot->s_rcsd, 103, 8);
+
+	return ((units[ts & 0x7]) * (mult[(ts >> 3) & 0xf]));
+}
+
+int
+sda_mem_parse_cid_csd(sda_slot_t *slot, dev_info_t *dip)
+{
+	uint32_t	*rcid;
+	uint32_t	*rcsd;
+	int		csdver;
+	uint16_t	rblen;
+	uint16_t	bshift;
+	uint32_t	cmult;
+	uint32_t	csize;
+	char		date[16];
+	char		*dtype;
+
+	rcid = slot->s_rcid;
+	rcsd = slot->s_rcsd;
+
+	csdver = sda_mem_getbits(rcsd, 127, 2);
+
+	if (slot->s_flags & SLOTF_SDMEM) {
+		switch (csdver) {
+		case 0:
+			csize = sda_mem_getbits(rcsd, 73, 12);
+			/* see comment above */
+			rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
+			cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
+			bshift = 9;
+			break;
+		case 1:
+			rblen = 512;
+			csize = sda_mem_getbits(rcsd, 69, 22);
+			cmult = 1024;
+			bshift = 0;
+			break;
+		default:
+			sda_slot_err(slot, "Unknown SD CSD version (%d)",
+			    csdver);
+			return (DDI_FAILURE);
+		}
+
+		dtype = slot->s_flags & SLOTF_SDHC ? "sdhc" : "sdcard";
+		slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
+		sda_mem_getstring(rcid, slot->s_oem, 119, 2);
+		sda_mem_getstring(rcid, slot->s_prod, 103, 5);
+		slot->s_majver = sda_mem_getbits(rcid, 63, 4);
+		slot->s_minver = sda_mem_getbits(rcid, 59, 4);
+		slot->s_serial =  sda_mem_getbits(rcid, 55, 32);
+		slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000;
+		slot->s_month = sda_mem_getbits(rcid, 11, 4);
+
+	} else if (slot->s_flags & SLOTF_MMC) {
+		if ((csdver < 1) || (csdver > 2)) {
+			sda_slot_err(slot, "Unknown MMC CSD version (%d)",
+			    csdver);
+			return (DDI_FAILURE);
+		}
+
+		dtype = "mmc";
+
+		switch (sda_mem_getbits(rcsd, 125, 4)) {
+		case 0:	/* MMC 1.0 - 1.2 */
+		case 1:	/* MMC 1.4 */
+			slot->s_mfg = sda_mem_getbits(rcid, 127, 24);
+			slot->s_oem[0] = 0;
+			sda_mem_getstring(rcid, slot->s_prod, 103, 7);
+			slot->s_majver = sda_mem_getbits(rcid, 47, 4);
+			slot->s_minver = sda_mem_getbits(rcid, 43, 4);
+			slot->s_serial =  sda_mem_getbits(rcid, 39, 24);
+			break;
+
+		case 2:	/* MMC 2.0 - 2.2 */
+		case 3:	/* MMC 3.1 - 3.3 */
+		case 4:	/* MMC 4.x */
+			slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
+			sda_mem_getstring(rcid, slot->s_oem, 119, 2);
+			sda_mem_getstring(rcid, slot->s_prod, 103, 6);
+			slot->s_majver = sda_mem_getbits(rcid, 55, 4);
+			slot->s_minver = sda_mem_getbits(rcid, 51, 4);
+			slot->s_serial =  sda_mem_getbits(rcid, 47, 32);
+			break;
+
+		default:
+			/* this error isn't fatal to us */
+			sda_slot_err(slot, "Unknown MMCA version (%d)",
+			    sda_mem_getbits(rcsd, 125, 4));
+			break;
+		}
+
+		slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997;
+		slot->s_month = sda_mem_getbits(rcid, 15, 4);
+
+		csize = sda_mem_getbits(rcsd, 73, 12);
+		rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
+		cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
+		bshift = 9;
+
+	} else {
+
+		sda_slot_err(slot, "Card type unknown");
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * These fields are common to all known MMC/SDcard memory cards.
+	 *
+	 * The spec requires that block size 512 be supported.
+	 * The media may have a different native size, but 512
+	 * byte blocks will always work.  This is true for SDcard,
+	 * and apparently for MMC as well.
+	 */
+	rblen = max(rblen, 512);	/* paranoia */
+	slot->s_nblks = (csize + 1) * cmult * (rblen / 512);
+	slot->s_bshift = bshift;
+	slot->s_blksz = 512;
+
+	slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3));
+	slot->s_ccc = sda_mem_getbits(rcsd, 95, 12);
+	slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1);
+	slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1);
+	slot->s_dsr = sda_mem_getbits(rcsd, 76, 1);
+
+	if (((slot->s_ccc & (1 << 4)) == 0) ||
+	    (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) {
+		slot->s_flags &= ~SLOTF_WRITABLE;
+	}
+	(void) snprintf(date, sizeof (date), "%02d-%04d",
+	    slot->s_month, slot->s_year);
+
+#define	prop_set_int(name, val)		\
+	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, name, val)
+#define	prop_set_str(name, val)		\
+	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, name, val)
+#define	prop_set_bool(name, val)	\
+	if (val) (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 0, name, NULL, 0)
+
+	prop_set_str("device-type", dtype);
+	prop_set_int("mfg-id", slot->s_mfg);
+	prop_set_str("product-id", slot->s_prod);
+	prop_set_str("oem-id", slot->s_oem);
+	prop_set_str("mfg-date", date);
+
+	prop_set_int("block-size", slot->s_blksz);
+	prop_set_int("num-blocks", slot->s_nblks);
+	prop_set_int("max-freq", slot->s_maxclk);
+	prop_set_bool("dsr-implemented", slot->s_dsr);
+	prop_set_int("ccc", slot->s_ccc);
+	prop_set_bool("perm-wp", slot->s_perm_wp);
+	prop_set_bool("temp-wp", slot->s_temp_wp);
+
+#undef	prop_set_int
+#undef	prop_set_str
+#undef	prop_set_bool
+
+	return (DDI_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/sda_mod.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SD card module support.
+ */
+
+#include <sys/modctl.h>
+#include <sys/sdcard/sda_impl.h>
+
+/*
+ * Static Variables.
+ */
+
+static struct modlmisc modlmisc = {
+	&mod_miscops,
+	"SD Card Architecture",
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, { &modlmisc, NULL }
+};
+
+/*
+ * DDI entry points.
+ */
+
+int
+_init(void)
+{
+	int	rv;
+
+	sda_cmd_init();
+	sda_nexus_init();
+
+	if ((rv = mod_install(&modlinkage)) != 0) {
+		sda_cmd_fini();
+		sda_nexus_fini();
+	}
+
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int	rv;
+
+	if ((rv = mod_remove(&modlinkage)) == 0) {
+		sda_cmd_fini();
+		sda_nexus_fini();
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/sda_nexus.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,930 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SD card nexus support.
+ *
+ * NB that this file contains a fair bit of non-DDI compliant code.
+ * But writing a nexus driver would be impossible to do with only DDI
+ * compliant interfaces.
+ */
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/list.h>
+#include <sys/mkdev.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sdcard/sda.h>
+#include <sys/sdcard/sda_ioctl.h>
+#include <sys/sdcard/sda_impl.h>
+
+
+/*
+ * Local prototypes.
+ */
+
+static sda_host_t *sda_nexus_lookup_dev(dev_t);
+static dev_info_t *sda_nexus_get_child(sda_slot_t *);
+static int sda_nexus_ap_ioctl(sda_host_t *, int, int, intptr_t);
+static int sda_nexus_ap_control(sda_host_t *, int, intptr_t, int);
+static int sda_nexus_ap_disconnect(sda_slot_t *);
+static int sda_nexus_ap_configure(sda_slot_t *);
+static int sda_nexus_ap_unconfigure(sda_slot_t *);
+static void sda_nexus_ap_getstate(sda_slot_t *, devctl_ap_state_t *);
+static void sda_nexus_reinsert(sda_slot_t *);
+static void sda_nexus_create(sda_slot_t *);
+
+/*
+ * Static Variables.
+ */
+
+static kmutex_t	sda_nexus_lock;
+static list_t	sda_nexus_list;
+
+/*
+ * Minor number allocation.
+ *
+ * We have up to NBITSMINOR32 (18) bits available.
+ *
+ * For each instance, we need one minor number for each slot, and one
+ * minor number for the devctl node.
+ *
+ * For simplicity's sake, we use the lower 8 bits for AP and DEVCTL nodes,
+ * and the remaining 10 bits for the instance number.
+ */
+#define	MINOR_DC		0xff
+#define	DEV_SLOT(dev)		(getminor(dev) & 0xff)
+#define	DEV_INST(dev)		(getminor(dev) >> 8)
+#define	MKMINOR_AP(inst, slot)	(((slot) & 0xff) | ((inst) << 8))
+#define	MKMINOR_DC(inst)	(((inst) << 8) | MINOR_DC)
+
+/*
+ * Implementation.
+ */
+
+void
+sda_nexus_init(void)
+{
+	list_create(&sda_nexus_list, sizeof (sda_host_t),
+	    offsetof(struct sda_host, h_node));
+	mutex_init(&sda_nexus_lock, NULL, MUTEX_DRIVER, NULL);
+}
+
+void
+sda_nexus_fini(void)
+{
+	list_destroy(&sda_nexus_list);
+	mutex_destroy(&sda_nexus_lock);
+}
+
+int
+sda_nexus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
+    void *arg, void *result)
+{
+	switch (ctlop) {
+	case DDI_CTLOPS_REPORTDEV:
+	{
+		cmn_err(CE_CONT, "?SD-device: %s@%s, %s#%d\n",
+		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
+		    ddi_driver_name(rdip), ddi_get_instance(rdip));
+
+		return (DDI_SUCCESS);
+	}
+
+	case DDI_CTLOPS_INITCHILD:
+	{
+		dev_info_t	*child_dip = (dev_info_t *)arg;
+		dev_info_t	*ndip;
+		sda_slot_t	*slot;
+		char		addr[16];
+
+		if ((slot = ddi_get_parent_data(child_dip)) == NULL) {
+			sda_slot_err(NULL, "Parent data struct missing!");
+			return (DDI_FAILURE);
+		}
+
+		/*
+		 * TODO: SDIO: We will need to use x,y addresses for
+		 * SDIO function numbers.  Memory cards will always
+		 * resid at address 0.  Probably this can be passed in
+		 * to this function using properties.
+		 */
+		(void) snprintf(addr, sizeof (addr), "%x", slot->s_slot_num);
+
+		/*
+		 * Prevent duplicate nodes.
+		 */
+		ndip = ndi_devi_find(dip, ddi_node_name(child_dip), addr);
+		if (ndip && (ndip != child_dip)) {
+			return (DDI_NOT_WELL_FORMED);
+		}
+
+		/*
+		 * Stash the address in the devinfo node.
+		 */
+		ddi_set_name_addr(child_dip, addr);
+
+		return (DDI_SUCCESS);
+	}
+
+	case DDI_CTLOPS_UNINITCHILD:
+	{
+		dev_info_t	*child_dip = (dev_info_t *)arg;
+
+		ddi_set_name_addr(child_dip, NULL);
+		ndi_prop_remove_all(child_dip);
+		return (DDI_SUCCESS);
+	}
+
+	case DDI_CTLOPS_SIDDEV:
+		/*
+		 * All SDA target devices are self-identifying.
+		 */
+		return (DDI_SUCCESS);
+
+	case DDI_CTLOPS_SLAVEONLY:
+		/*
+		 * We don't support DMA master for SDA targets.
+		 */
+		return (DDI_SUCCESS);
+
+	case DDI_CTLOPS_AFFINITY:
+		/*
+		 * NB: We may want to revisit this later, so that functions
+		 * on one card can see other functions on the same card.
+		 * Right now there is no need.
+		 */
+		return (DDI_FAILURE);
+
+	case DDI_CTLOPS_DMAPMAPC:
+	case DDI_CTLOPS_REPORTINT:
+	case DDI_CTLOPS_POKE:
+	case DDI_CTLOPS_PEEK:
+	case DDI_CTLOPS_NREGS:
+	case DDI_CTLOPS_REGSIZE:
+		/*
+		 * We don't support any of these (yet?).
+		 */
+		return (DDI_FAILURE);
+
+	default:
+		/*
+		 * Everything else goes to the parent nexus.
+		 */
+		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+	}
+}
+
+void
+sda_nexus_register(sda_host_t *h)
+{
+	int	i;
+	int	inst;
+	char	name[16];
+
+	mutex_enter(&sda_nexus_lock);
+	list_insert_tail(&sda_nexus_list, h);
+	mutex_exit(&sda_nexus_lock);
+
+	/*
+	 * Now create minor nodes.  Note that failures to create these nodes
+	 * are mostly harmless, so we don't do much besides warn about it.
+	 * (It means cfgadm will be useless, but most folks aren't likely
+	 * to use cfgadm anyway.)
+	 */
+
+	inst = ddi_get_instance(h->h_dip);
+
+	/*
+	 * Create the devctl minor node.
+	 */
+	if (ddi_create_minor_node(h->h_dip, "devctl", S_IFCHR,
+	    MKMINOR_DC(inst), DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
+		sda_slot_err(NULL, "Unable to create devctl node");
+	}
+
+	for (i = 0; i < h->h_nslot; i++) {
+
+		sda_slot_t	*slot;
+
+		slot = &h->h_slots[i];
+		/*
+		 * Create the attachment point minor nodes.
+		 */
+		(void) snprintf(name, sizeof (name), "%d", i);
+		if (ddi_create_minor_node(h->h_dip, name, S_IFCHR,
+		    MKMINOR_AP(inst, i), DDI_NT_SDCARD_ATTACHMENT_POINT,
+		    0) != DDI_SUCCESS) {
+			sda_slot_err(slot,
+			    "Unable to create attachment point node");
+		}
+	}
+}
+
+void
+sda_nexus_unregister(sda_host_t *h)
+{
+	/*
+	 * Remove all minor nodes.
+	 */
+	ddi_remove_minor_node(h->h_dip, NULL);
+
+	mutex_enter(&sda_nexus_lock);
+	list_remove(&sda_nexus_list, h);
+	mutex_exit(&sda_nexus_lock);
+}
+
+sda_host_t *
+sda_nexus_lookup_dev(dev_t dev)
+{
+	major_t		maj;
+	int		inst;
+	sda_host_t	*h;
+
+	ASSERT(mutex_owned(&sda_nexus_lock));
+
+	maj = getmajor(dev);
+	inst = DEV_INST(dev);
+
+	h = list_head(&sda_nexus_list);
+	while (h != NULL) {
+		if ((ddi_driver_major(h->h_dip) == maj) &&
+		    (ddi_get_instance(h->h_dip) == inst)) {
+			break;
+		}
+		h = list_next(&sda_nexus_list, h);
+	}
+	return (h);
+}
+
+void
+sda_nexus_create(sda_slot_t *slot)
+{
+	dev_info_t	*pdip, *cdip;
+	int		rv;
+
+	pdip = slot->s_host->h_dip;
+
+	/*
+	 * SDIO: This whole function will need to be recrafted to
+	 * support non-memory children.  For SDIO, there could be
+	 * multiple functions, which get inserted or removed together.
+	 */
+
+	if (ndi_devi_alloc(pdip, "sdcard", DEVI_SID_NODEID, &cdip) !=
+	    NDI_SUCCESS) {
+		sda_slot_err(slot, "Failed allocating devinfo node");
+		return;
+	}
+
+	ddi_set_parent_data(cdip, slot);
+
+	/*
+	 * Make sure the child node gets suspend/resume events.
+	 */
+	rv = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "pm-capable", 1);
+	if (rv != 0) {
+		sda_slot_err(slot, "Failed creating pm-capable property");
+		(void) ndi_devi_free(cdip);
+		return;
+	}
+
+	sda_slot_enter(slot);
+	slot->s_ready = B_TRUE;
+	sda_slot_exit(slot);
+
+	if (ndi_devi_online(cdip, NDI_ONLINE_ATTACH) != NDI_SUCCESS) {
+		sda_slot_err(slot, "Failed bringing node online");
+		(void) ndi_devi_free(cdip);
+	}
+}
+
+void
+sda_nexus_reinsert(sda_slot_t *slot)
+{
+	dev_info_t	*cdip, *ndip, *pdip;
+	int		circ;
+
+	pdip = slot->s_host->h_dip;
+
+	ndi_devi_enter(pdip, &circ);
+	ndip = ddi_get_child(pdip);
+	while ((cdip = ndip) !=  NULL) {
+		ndip = ddi_get_next_sibling(cdip);
+		if (ddi_get_parent_data(cdip) == slot) {
+			mutex_enter(&DEVI(cdip)->devi_lock);
+			DEVI_SET_DEVICE_REINSERTED(cdip);
+			mutex_exit(&DEVI(cdip)->devi_lock);
+		}
+	}
+	ndi_devi_exit(pdip, circ);
+
+	sda_slot_enter(slot);
+	slot->s_warn = B_FALSE;
+	slot->s_ready = B_TRUE;
+	sda_slot_exit(slot);
+}
+
+void
+sda_nexus_insert(sda_slot_t *slot)
+{
+	char		uuid[40];
+	boolean_t	match;
+
+	if (slot->s_flags & SLOTF_MEMORY) {
+		(void) snprintf(uuid, sizeof (uuid), "%c%08X%08X%08X%08X",
+		    slot->s_flags & SLOTF_MMC ? 'M' : 'S',
+		    slot->s_rcid[0], slot->s_rcid[1],
+		    slot->s_rcid[2], slot->s_rcid[3]);
+	} else {
+		/*
+		 * SDIO: For SDIO, we can write the card's MANFID
+		 * tuple in CIS to the UUID.  Until we support SDIO,
+		 * we just suppress creating devinfo nodes.
+		 */
+		sda_slot_err(slot, "Non-memory target not supported");
+		uuid[0] = 0;
+	}
+
+	match = ((uuid[0] != 0) && (strcmp(slot->s_uuid, uuid) == 0));
+
+	if (sda_nexus_get_child(slot) != NULL) {
+		if (!match) {
+			sda_slot_err(slot, "Card removed while still in use.");
+			sda_slot_err(slot, "Please reinsert previous card.");
+
+			sda_nexus_remove(slot);
+		} else {
+			sda_nexus_reinsert(slot);
+		}
+	} else {
+		/*
+		 * Remember the UUID.
+		 */
+		(void) strlcpy(slot->s_uuid, uuid, sizeof (slot->s_uuid));
+		/*
+		 * Create the children.
+		 */
+		if (uuid[0] != 0)
+			sda_nexus_create(slot);
+	}
+}
+
+void
+sda_nexus_remove(sda_slot_t *slot)
+{
+	sda_host_t	*h  = slot->s_host;
+	dev_info_t	*pdip = h->h_dip;
+	dev_info_t	*cdip;
+	int		circ;
+	char		addr[16];
+	int		addrl;
+	char		*ap;
+	boolean_t	reap = B_FALSE;
+
+	ndi_devi_enter(pdip, &circ);
+	cdip = ddi_get_child(pdip);
+
+	/* calculate the prefix address that slot's children should have */
+	(void) snprintf(addr, sizeof (addr), "%x", slot->s_slot_num);
+	addrl = strlen(addr);
+
+	while (cdip != NULL) {
+		ap = ddi_get_name_addr(cdip);
+		if (ap == NULL)
+			continue;
+
+		if ((strncmp(addr, ap, addrl) != 0) ||
+		    ((ap[addrl] != '\0') && (ap[addrl] != ','))) {
+			/* address isn't for this slot */
+			continue;
+		}
+
+		reap = B_TRUE;
+		mutex_enter(&(DEVI(cdip))->devi_lock);
+		DEVI_SET_DEVICE_REMOVED(cdip);
+		mutex_exit(&(DEVI(cdip))->devi_lock);
+
+		cdip = ddi_get_next_sibling(cdip);
+	}
+	ndi_devi_exit(pdip, circ);
+
+	if (reap) {
+		sda_slot_enter(slot);
+		slot->s_reap = B_TRUE;
+		sda_slot_exit(slot);
+		sda_slot_wakeup(slot);
+	}
+}
+
+void
+sda_nexus_reap(void *arg)
+{
+	sda_slot_t	*slot = arg;
+	dev_info_t	*pdip = slot->s_host->h_dip;
+	dev_info_t	*cdip, *ndip;
+	int		circ;
+
+	ndi_devi_enter(pdip, &circ);
+	ndip = ddi_get_child(pdip);
+
+	/*
+	 * NB: The goofy locking order here is required because
+	 * ndi_devi_offline won't clean the devfs cache if the parent
+	 * lock is held.  There really needs to be a better way, such
+	 * as a recurse flag.
+	 */
+	while ((cdip = ndip) != NULL) {
+
+		/* get the next node before we delete this one! */
+		ndip = ddi_get_next_sibling(cdip);
+
+		if ((ddi_get_parent_data(cdip) == slot) &&
+		    (DEVI_IS_DEVICE_REMOVED(cdip))) {
+
+
+			ndi_devi_exit(pdip, circ);
+			if (ndi_devi_offline(cdip, NDI_DEVI_REMOVE) !=
+			    NDI_SUCCESS) {
+
+				mutex_enter(&slot->s_evlock);
+				slot->s_reap = B_TRUE;
+				mutex_exit(&slot->s_evlock);
+				return;
+			}
+
+			ndi_devi_enter(pdip, &circ);
+			/* we removed it, so restart from the beginning */
+			ndip = ddi_get_child(pdip);
+		}
+	}
+	mutex_enter(&slot->s_evlock);
+	/* woohoo, done reaping nodes */
+	slot->s_reap = B_FALSE;
+	mutex_exit(&slot->s_evlock);
+
+	ndi_devi_exit(pdip, circ);
+}
+
+dev_info_t *
+sda_nexus_get_child(sda_slot_t *slot)
+{
+	int		circ;
+	dev_info_t	*cdip, *pdip;
+
+	pdip = slot->s_host->h_dip;
+
+	ndi_devi_enter(pdip, &circ);
+	cdip = ddi_get_child(pdip);
+	while (cdip != NULL) {
+		if (ddi_get_parent_data(cdip) == slot) {
+			break;
+		}
+		cdip = ddi_get_next_sibling(cdip);
+	}
+	ndi_devi_exit(pdip, circ);
+	return (cdip);
+}
+
+
+/*ARGSUSED3*/
+int
+sda_nexus_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+	int		rv = 0;
+	sda_host_t	*h;
+
+	if (otyp != OTYP_CHR)
+		return (EINVAL);
+
+	mutex_enter(&sda_nexus_lock);
+	if ((h = sda_nexus_lookup_dev(*devp)) == NULL) {
+		mutex_exit(&sda_nexus_lock);
+		return (ENXIO);
+	}
+
+	if (flags & FEXCL) {
+		if ((h->h_flags & (HOST_SOPEN|HOST_XOPEN)) != 0) {
+			rv = EBUSY;
+		} else {
+			h->h_flags |= HOST_XOPEN;
+		}
+	} else {
+		if ((h->h_flags & HOST_XOPEN) != 0) {
+			rv = EBUSY;
+		} else {
+			h->h_flags |= HOST_SOPEN;
+		}
+	}
+	mutex_exit(&sda_nexus_lock);
+	return (rv);
+}
+
+/*ARGSUSED1*/
+int
+sda_nexus_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+	sda_host_t	*h;
+
+	if (otyp != OTYP_CHR)
+		return (EINVAL);
+
+	mutex_enter(&sda_nexus_lock);
+	if ((h = sda_nexus_lookup_dev(dev)) == NULL) {
+		mutex_exit(&sda_nexus_lock);
+		return (ENXIO);
+	}
+	h->h_flags &= ~(HOST_XOPEN | HOST_SOPEN);
+	mutex_exit(&sda_nexus_lock);
+	return (0);
+}
+
+void
+sda_nexus_ap_getstate(sda_slot_t *slot, devctl_ap_state_t *ap_state)
+{
+	dev_info_t	*cdip;
+	int		circ;
+
+	ndi_devi_enter(slot->s_host->h_dip, &circ);
+
+	/*
+	 * Default state.
+	 */
+	ap_state->ap_rstate = AP_RSTATE_EMPTY;
+	ap_state->ap_condition = AP_COND_OK;
+	ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
+
+	if (slot->s_inserted) {
+		ap_state->ap_rstate = AP_RSTATE_CONNECTED;
+	}
+
+	if ((cdip = sda_nexus_get_child(slot)) != NULL) {
+		mutex_enter(&DEVI(cdip)->devi_lock);
+		if (DEVI_IS_DEVICE_REMOVED(cdip)) {
+			ap_state->ap_condition = AP_COND_UNUSABLE;
+		}
+		if (DEVI_IS_DEVICE_OFFLINE(cdip) ||
+		    DEVI_IS_DEVICE_DOWN(cdip)) {
+			ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
+		} else {
+			ap_state->ap_ostate = AP_OSTATE_CONFIGURED;
+		}
+		mutex_exit(&DEVI(cdip)->devi_lock);
+	}
+
+	if (slot->s_failed) {
+		ap_state->ap_condition = AP_COND_FAILED;
+	}
+
+	ap_state->ap_last_change = slot->s_stamp;
+	ap_state->ap_in_transition = slot->s_intransit;
+
+	ndi_devi_exit(slot->s_host->h_dip, circ);
+}
+
+int
+sda_nexus_ap_disconnect(sda_slot_t *slot)
+{
+	dev_info_t	*cdip;
+
+	/* if a child node exists, try to delete it */
+	if ((cdip = sda_nexus_get_child(slot)) != NULL) {
+		if (ndi_devi_offline(cdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) {
+			/* couldn't disconnect, why not? */
+			return (EBUSY);
+		}
+		slot->s_stamp = ddi_get_time();
+	}
+	return (0);
+}
+
+int
+sda_nexus_ap_unconfigure(sda_slot_t *slot)
+{
+	dev_info_t	*cdip;
+
+	/* attempt to unconfigure the node */
+	if ((cdip = sda_nexus_get_child(slot)) == NULL) {
+		/* node not there! */
+		return (ENXIO);
+	}
+
+	if (ndi_devi_offline(cdip, NDI_UNCONFIG) != NDI_SUCCESS) {
+		/* failed to unconfigure the node (EBUSY?) */
+		return (EIO);
+	}
+	slot->s_stamp = ddi_get_time();
+	return (0);
+}
+
+int
+sda_nexus_ap_configure(sda_slot_t *slot)
+{
+	dev_info_t	*cdip;
+
+	sda_slot_enter(slot);
+	if (slot->s_inserted == B_FALSE) {
+		/* device not present */
+		sda_slot_exit(slot);
+		return (ENXIO);
+	}
+
+	/* attempt to configure the node */
+	if ((cdip = sda_nexus_get_child(slot)) == NULL) {
+		sda_slot_exit(slot);
+		/* node not there! */
+		return (ENXIO);
+	}
+	sda_slot_exit(slot);
+
+	slot->s_intransit = 1;
+	if (ndi_devi_online(cdip, NDI_CONFIG) != NDI_SUCCESS) {
+		/* failed to configure the node */
+		slot->s_intransit = 0;
+		return (EIO);
+	}
+	slot->s_intransit = 0;
+	slot->s_stamp = ddi_get_time();
+	return (0);
+}
+
+int
+sda_nexus_ap_ioctl(sda_host_t *h, int snum, int cmd, intptr_t arg)
+{
+	struct devctl_iocdata	*dcp = NULL;
+	devctl_ap_state_t	ap_state;
+	sda_slot_t		*slot;
+	int			rv = 0;
+
+	/*
+	 * In theory we could try to support this operation on the
+	 * DEVCTL minor, but then we would need a slot member in the
+	 * user nvlist.  For now its easiest to assume a 1:1 relation
+	 * between the AP minor node, and the slot number.
+	 */
+	if (snum >= h->h_nslot) {
+		return (ENXIO);
+	}
+	slot = &h->h_slots[snum];
+
+	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
+		return (EFAULT);
+
+	switch (cmd) {
+	case DEVCTL_AP_DISCONNECT:
+		rv = sda_nexus_ap_disconnect(slot);
+		break;
+
+	case DEVCTL_AP_UNCONFIGURE:
+		rv = sda_nexus_ap_unconfigure(slot);
+		break;
+
+	case DEVCTL_AP_CONFIGURE:
+		rv = sda_nexus_ap_configure(slot);
+		break;
+
+	case DEVCTL_AP_GETSTATE:
+		sda_nexus_ap_getstate(slot, &ap_state);
+		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
+			rv = EFAULT;
+		}
+		break;
+	}
+
+	ndi_dc_freehdl(dcp);
+
+	return (rv);
+}
+
+int
+sda_nexus_ap_control(sda_host_t *h, int snum, intptr_t arg, int mode)
+{
+	struct sda_ap_control	apc;
+	struct sda_ap_control32	apc32;
+	sda_slot_t		*slot;
+	int			rv = 0;
+
+	if (snum >= h->h_nslot) {
+		return (ENXIO);
+	}
+	slot = &h->h_slots[snum];
+
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+	case DDI_MODEL_ILP32:
+		if (ddi_copyin((void *)arg, &apc32, sizeof (apc32), mode) !=
+		    0) {
+			return (EFAULT);
+		}
+		apc.cmd = apc32.cmd;
+		apc.size = apc32.size;
+		apc.data = (caddr_t *)(intptr_t)apc32.data;
+		break;
+	case DDI_MODEL_NONE:
+		if (ddi_copyin((void *)arg, &apc, sizeof (apc), mode) != 0) {
+			return (EFAULT);
+		}
+		break;
+	}
+
+	switch (apc.cmd) {
+	case SDA_CFGA_GET_CARD_INFO: {
+		sda_card_info_t	ci;
+
+		if (apc.size < sizeof (sda_card_info_t)) {
+			apc.size = sizeof (sda_card_info_t);
+			break;
+		}
+		sda_slot_enter(slot);
+		if (!slot->s_inserted) {
+			ci.ci_type = SDA_CT_UNKNOWN;
+		} else if (slot->s_flags & SLOTF_MMC) {
+			ci.ci_type = SDA_CT_MMC;
+		} else if (slot->s_flags & SLOTF_SDIO) {
+			if (slot->s_flags & SLOTF_MEMORY) {
+				ci.ci_type = SDA_CT_SDCOMBO;
+			} else {
+				ci.ci_type = SDA_CT_SDIO;
+			}
+		} else if (slot->s_flags & SLOTF_SDMEM) {
+			if (slot->s_flags & SLOTF_SDHC) {
+				ci.ci_type = SDA_CT_SDHC;
+			} else {
+				ci.ci_type = SDA_CT_SDMEM;
+			}
+		} else {
+			ci.ci_type = SDA_CT_UNKNOWN;
+		}
+
+		if (slot->s_flags & SLOTF_MEMORY) {
+			ci.ci_mfg = slot->s_mfg;
+			(void) strlcpy(ci.ci_oem,
+			    slot->s_oem, sizeof (ci.ci_oem));
+			(void) strlcpy(ci.ci_pid,
+			    slot->s_prod, sizeof (ci.ci_pid));
+			ci.ci_serial = slot->s_serial;
+			ci.ci_month = slot->s_month;
+			ci.ci_year = (slot->s_year - 1900) & 0xff;
+			ci.ci_major = slot->s_majver;
+			ci.ci_minor = slot->s_minver;
+		}
+
+		sda_slot_exit(slot);
+
+		if (ddi_copyout(&ci, apc.data, sizeof (ci), mode) != 0) {
+			return (EFAULT);
+		}
+
+		break;
+	}
+
+	case SDA_CFGA_GET_DEVICE_PATH:
+	{
+		char		path[MAXPATHLEN];
+		dev_info_t	*cdip;
+		int		slen;
+
+		if ((cdip = sda_nexus_get_child(slot)) == NULL) {
+			return (ENOENT);
+		}
+		(void) strcpy(path, "/devices");
+		(void) ddi_pathname(cdip, path + strlen(path));
+		slen = strlen(path) + 1;
+		if (apc.size < slen) {
+			apc.size = slen;
+			rv = ENOSPC;
+			break;
+		}
+		apc.size = slen;
+		if (ddi_copyout(path, apc.data, slen, mode) != 0) {
+			return (EFAULT);
+		}
+		break;
+	}
+
+	case SDA_CFGA_RESET_SLOT:
+	{
+		sda_slot_enter(slot);
+		slot->s_failed = B_FALSE;
+		sda_slot_exit(slot);
+		sda_slot_reset(slot);
+		sda_slot_detect(slot);
+		break;
+	}
+
+	default:
+		return (EINVAL);
+	}
+
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+	case DDI_MODEL_ILP32:
+		apc32.cmd = apc.cmd;
+		apc32.size = (size32_t)apc.size;
+		apc32.data = (caddr32_t)(intptr_t)apc.data;
+		if (ddi_copyout(&apc32, (void *)arg, sizeof (apc32), mode) !=
+		    0) {
+			return (EFAULT);
+		}
+		break;
+	case DDI_MODEL_NONE:
+		if (ddi_copyout(&apc, (void *)arg, sizeof (apc), mode) != 0) {
+			return (EFAULT);
+		}
+		break;
+	}
+	return (rv);
+}
+
+/*ARGSUSED4*/
+int
+sda_nexus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+    int *rvp)
+{
+	sda_host_t	*h;
+
+	mutex_enter(&sda_nexus_lock);
+	h = sda_nexus_lookup_dev(dev);
+	mutex_exit(&sda_nexus_lock);
+
+	if (h == NULL)
+		return (ENXIO);
+
+	switch (cmd) {
+	case DEVCTL_DEVICE_GETSTATE:
+	case DEVCTL_DEVICE_ONLINE:
+	case DEVCTL_DEVICE_OFFLINE:
+	case DEVCTL_DEVICE_REMOVE:
+	case DEVCTL_BUS_GETSTATE:
+		return (ndi_devctl_ioctl(h->h_dip, cmd, arg, mode, 0));
+
+	case DEVCTL_AP_DISCONNECT:
+	case DEVCTL_AP_CONFIGURE:
+	case DEVCTL_AP_UNCONFIGURE:
+	case DEVCTL_AP_GETSTATE:
+		return (sda_nexus_ap_ioctl(h, DEV_SLOT(dev), cmd, arg));
+
+	case DEVCTL_AP_CONTROL:
+		return (sda_nexus_ap_control(h, DEV_SLOT(dev), arg, mode));
+
+	default:
+		return (ENOTSUP);
+	}
+}
+
+/*ARGSUSED*/
+int
+sda_nexus_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
+{
+	sda_host_t	*h;
+	int		rv;
+
+	rv = DDI_FAILURE;
+
+	switch (cmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		mutex_enter(&sda_nexus_lock);
+		h = sda_nexus_lookup_dev((dev_t)arg);
+		if (h != NULL) {
+			*resp = h->h_dip;
+			rv = DDI_SUCCESS;
+		}
+		mutex_exit(&sda_nexus_lock);
+		break;
+
+	case DDI_INFO_DEVT2INSTANCE:
+		*resp = (void *)(intptr_t)DEV_INST((dev_t)arg);
+		rv = DDI_SUCCESS;
+		break;
+	}
+	return (rv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/impl/sda_slot.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,896 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SD card slot support.
+ *
+ * NB that this file contains a fair bit of non-DDI compliant code.
+ * But writing a nexus driver would be impossible to do with only DDI
+ * compliant interfaces.
+ */
+
+#include <sys/types.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/callb.h>
+#include <sys/sysmacros.h>
+#include <sys/cpuvar.h>
+#include <sys/cmn_err.h>
+#include <sys/varargs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sdcard/sda_impl.h>
+
+
+/*
+ * Prototypes.
+ */
+
+static void sda_slot_insert(void *);
+static sda_err_t sda_slot_check_response(sda_cmd_t *);
+static void sda_slot_handle_detect(sda_slot_t *);
+static void sda_slot_handle_transfer(sda_slot_t *, sda_err_t);
+static void sda_slot_handle_fault(sda_slot_t *, sda_fault_t);
+static void sda_slot_abort(sda_slot_t *, sda_err_t);
+static void sda_slot_halt(sda_slot_t *);
+static void sda_slot_thread(void *);
+static void sda_slot_vprintf(sda_slot_t *, int, const char *, va_list);
+
+/*
+ * Static Variables.
+ */
+
+static struct {
+	sda_fault_t	fault;
+	const char	*msg;
+} sda_slot_faults[] = {
+	{ SDA_FAULT_TIMEOUT,	"Data transfer timed out" },
+	{ SDA_FAULT_ACMD12,	"Auto CMD12 failure" },
+	{ SDA_FAULT_CRC7,	"CRC7 failure on CMD/DAT line" },
+	{ SDA_FAULT_PROTO,	"SD/MMC protocol signaling error" },
+	{ SDA_FAULT_INIT,	"Card initialization failure" },
+	{ SDA_FAULT_HOST,	"Internal host or slot failure" },
+	{ SDA_FAULT_CURRENT,	"Current overlimit detected" },
+	{ SDA_FAULT_RESET,	"Failed to reset slot" },
+	{ SDA_FAULT_NONE,	NULL },	/* sentinel, must be last! */
+};
+
+/*
+ * Internal implementation.
+ */
+
+/*
+ * These allow for recursive entry.  This is necessary to facilitate
+ * simpler locking with things like the fault handler, where a caller
+ * might already be "holding" the slot.
+ *
+ * This is modeled in part after ndi_devi_enter and ndi_devi_exit.
+ */
+void
+sda_slot_enter(sda_slot_t *slot)
+{
+	kt_did_t	self = ddi_get_kt_did();
+	mutex_enter(&slot->s_lock);
+	if (slot->s_owner == self) {
+		slot->s_circular++;
+	} else {
+		while ((slot->s_owner != 0) && (slot->s_owner != self)) {
+			cv_wait(&slot->s_cv, &slot->s_lock);
+		}
+		slot->s_owner = self;
+		slot->s_circular++;
+	}
+	mutex_exit(&slot->s_lock);
+}
+
+void
+sda_slot_exit(sda_slot_t *slot)
+{
+	ASSERT(sda_slot_owned(slot));
+
+	mutex_enter(&slot->s_lock);
+	slot->s_circular--;
+	if (slot->s_circular == 0) {
+		slot->s_owner = 0;
+		cv_broadcast(&slot->s_cv);
+	}
+	mutex_exit(&slot->s_lock);
+}
+
+boolean_t
+sda_slot_owned(sda_slot_t *slot)
+{
+	return (slot->s_owner == ddi_get_kt_did());
+}
+
+sda_err_t
+sda_slot_check_response(sda_cmd_t *cmdp)
+{
+	uint32_t	errs;
+	switch (cmdp->sc_rtype & 0xf) {
+	case R1:
+		if ((errs = (cmdp->sc_response[0] & R1_ERRS)) != 0) {
+			if (errs & (R1_WP_VIOLATION | R1_CSD_OVERWRITE)) {
+				return (SDA_EWPROTECT);
+			}
+			if (errs & (R1_ADDRESS_ERROR | R1_BLOCK_LEN_ERROR |
+			    R1_OUT_OF_RANGE | R1_ERASE_PARAM)) {
+				return (SDA_EINVAL);
+			}
+			return (SDA_EIO);
+		}
+		break;
+	case R5:
+		if ((errs = (cmdp->sc_response[0] & R5_ERRS)) != 0) {
+			return (SDA_EIO);
+		}
+		break;
+	}
+	return (SDA_EOK);
+}
+
+void
+sda_slot_halt(sda_slot_t *slot)
+{
+	sda_slot_enter(slot);
+	slot->s_ops.so_halt(slot->s_prv);
+	drv_usecwait(1000);	/* we need to wait 1 msec for power down */
+	sda_slot_exit(slot);
+}
+
+void
+sda_slot_reset(sda_slot_t *slot)
+{
+	sda_slot_enter(slot);
+	if (slot->s_ops.so_reset(slot->s_prv) != 0) {
+		sda_slot_fault(slot, SDA_FAULT_RESET);
+	}
+	sda_slot_exit(slot);
+}
+
+int
+sda_slot_power_on(sda_slot_t *slot)
+{
+	int		rv;
+	uint32_t	ocr;
+
+	sda_slot_enter(slot);
+
+	/*
+	 * Get the voltage supplied by the host.  Note that we expect
+	 * hosts will include a range of 2.7-3.7 in their supported
+	 * voltage ranges.  The spec does not allow for hosts that
+	 * cannot supply a voltage in this range, yet.
+	 */
+	if ((rv = sda_getprop(slot, SDA_PROP_OCR, &ocr)) != 0) {
+		sda_slot_err(slot, "Failed to get host OCR (%d)", rv);
+		goto done;
+	}
+	if ((ocr & OCR_HI_MASK) == 0) {
+		sda_slot_err(slot, "Host does not support standard voltages.");
+		rv = ENOTSUP;
+		goto done;
+	}
+
+	/*
+	 * We prefer 3.3V, 3.0V, and failing that, just use the
+	 * maximum that the host supports.  3.3V is preferable,
+	 * because it is the typical common voltage that just about
+	 * everything supports.  Otherwise we just pick the highest
+	 * supported voltage.  This facilitates initial power up.
+	 */
+	if (ocr & OCR_32_33V) {
+		slot->s_cur_ocr = OCR_32_33V;
+	} else if (ocr & OCR_29_30V) {
+		slot->s_cur_ocr = OCR_29_30V;
+	} else {
+		slot->s_cur_ocr = (1U << (ddi_fls(ocr) - 1));
+	}
+
+	/*
+	 * Turn on the power.
+	 */
+	if ((rv = sda_setprop(slot, SDA_PROP_OCR, slot->s_cur_ocr)) != 0) {
+		sda_slot_err(slot, "Failed to set OCR %x (%d)",
+		    slot->s_cur_ocr, rv);
+		goto done;
+	}
+
+	sda_slot_exit(slot);
+
+	/*
+	 * Wait 250 msec (per spec) for power ramp to complete.
+	 */
+	delay(drv_usectohz(250000));
+	return (0);
+
+done:
+	sda_slot_exit(slot);
+	return (rv);
+}
+
+void
+sda_slot_power_off(sda_slot_t *slot)
+{
+	sda_slot_enter(slot);
+	(void) sda_setprop(slot, SDA_PROP_OCR, 0);
+	/* XXX: FMA: on failure this should cause a fault to be generated */
+	/* spec requires voltage to stay low for at least 1 msec */
+	drv_usecwait(1000);
+	sda_slot_exit(slot);
+}
+
+void
+sda_slot_insert(void *arg)
+{
+	sda_slot_t	*slot = arg;
+
+	if (sda_init_card(slot) != SDA_EOK) {
+		/*
+		 * Remove power from the slot.  If a more severe fault
+		 * occurred, then a manual reset with cfgadm will be needed.
+		 */
+		sda_slot_err(slot, "Unable to initialize card!");
+		sda_slot_enter(slot);
+		sda_slot_power_off(slot);
+		sda_slot_abort(slot, SDA_ENODEV);
+		sda_slot_exit(slot);
+		sda_nexus_remove(slot);
+
+	} else {
+		sda_nexus_insert(slot);
+	}
+
+	slot->s_stamp = ddi_get_time();
+	slot->s_intransit = 0;
+}
+
+void
+sda_slot_mem_reset(sda_slot_t *slot, sda_err_t errno)
+{
+	sda_cmd_t	*cmdp;
+
+	sda_slot_enter(slot);
+	cmdp = list_head(&slot->s_cmdlist);
+	while (cmdp != NULL) {
+		sda_cmd_t	*next;
+		next = list_next(&slot->s_cmdlist, cmdp);
+		if (cmdp->sc_flags & SDA_CMDF_MEM) {
+			list_remove(&slot->s_cmdlist, cmdp);
+			sda_cmd_notify(cmdp, 0, errno);
+			mutex_enter(&slot->s_evlock);
+			list_insert_tail(&slot->s_abortlist, cmdp);
+			mutex_exit(&slot->s_evlock);
+		}
+		cmdp = next;
+	}
+	sda_slot_exit(slot);
+
+	/* wake up to process the abort list */
+	sda_slot_wakeup(slot);
+}
+
+void
+sda_slot_abort(sda_slot_t *slot, sda_err_t errno)
+{
+	sda_cmd_t	*cmdp;
+
+	ASSERT(sda_slot_owned(slot));
+
+	if ((cmdp = slot->s_xfrp) != NULL) {
+		slot->s_xfrp = NULL;
+		sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT, errno);
+	}
+	while ((cmdp = list_head(&slot->s_cmdlist)) != NULL) {
+		list_remove(&slot->s_cmdlist, cmdp);
+		sda_cmd_notify(cmdp, 0, errno);
+		mutex_enter(&slot->s_evlock);
+		list_insert_tail(&slot->s_abortlist, cmdp);
+		mutex_exit(&slot->s_evlock);
+	}
+
+	sda_slot_wakeup(slot);
+}
+
+void
+sda_slot_handle_transfer(sda_slot_t *slot, sda_err_t errno)
+{
+	sda_cmd_t	*cmdp;
+
+	sda_slot_enter(slot);
+
+	if ((cmdp = slot->s_xfrp) != NULL) {
+
+		slot->s_xfrp = NULL;
+		slot->s_xfrtmo = 0;
+		(void) sda_setprop(slot, SDA_PROP_LED, 0);
+		sda_slot_exit(slot);
+
+		sda_slot_wakeup(slot);
+
+		sda_cmd_notify(cmdp, SDA_CMDF_DAT, errno);
+	} else {
+		sda_slot_exit(slot);
+	}
+}
+
+void
+sda_slot_handle_fault(sda_slot_t *slot, sda_fault_t fault)
+{
+	const char	*msg;
+	int		i;
+
+	sda_slot_enter(slot);
+
+	if ((fault == SDA_FAULT_TIMEOUT) && (slot->s_init)) {
+		/*
+		 * Timeouts during initialization are quite normal.
+		 */
+		sda_slot_exit(slot);
+		return;
+	}
+
+	slot->s_failed = B_TRUE;
+	sda_slot_abort(slot, SDA_EFAULT);
+
+	msg = "Unknown fault (%d)";
+	for (i = 0; sda_slot_faults[i].msg != NULL; i++) {
+		if (sda_slot_faults[i].fault == fault) {
+			msg = sda_slot_faults[i].msg;
+			break;
+		}
+	}
+
+	/*
+	 * FMA would be a better choice here.
+	 */
+	sda_slot_err(slot, msg, fault);
+
+	/*
+	 * Shut down the slot.  Interaction from userland via cfgadm
+	 * can revive it.
+	 *
+	 * FMA can help here.
+	 */
+	sda_slot_halt(slot);
+
+	sda_slot_exit(slot);
+}
+
+void
+sda_slot_handle_detect(sda_slot_t *slot)
+{
+	uint32_t	inserted;
+
+	sda_slot_enter(slot);
+
+	slot->s_stamp = ddi_get_time();
+	slot->s_intransit = 1;
+	slot->s_flags = 0;
+	slot->s_rca = 0;
+	slot->s_ready = B_FALSE;
+
+	sda_getprop(slot, SDA_PROP_INSERTED, &inserted);
+	slot->s_inserted = (inserted != 0);
+
+	if (slot->s_inserted && !slot->s_failed) {
+		/*
+		 * We need to initialize the card, so we only support
+		 * hipri commands for now.
+		 */
+		slot->s_init = B_TRUE;
+
+		/*
+		 * Card insertion occurred.  We have to run this on
+		 * another task, to avoid deadlock as the task may
+		 * need to dispatch commands.
+		 */
+		(void) ddi_taskq_dispatch(slot->s_tq, sda_slot_insert, slot,
+		    DDI_SLEEP);
+	} else {
+
+		/*
+		 * Nuke in-flight commands.
+		 */
+		sda_slot_abort(slot, SDA_ENODEV);
+
+		/*
+		 * Restart the slot (incl. power cycle).  This gets the
+		 * slot to a known good state.
+		 */
+		sda_slot_reset(slot);
+
+		sda_nexus_remove(slot);
+
+		slot->s_intransit = 0;
+	}
+	sda_slot_exit(slot);
+
+	sda_slot_wakeup(slot);
+}
+
+void
+sda_slot_transfer(sda_slot_t *slot, sda_err_t errno)
+{
+	mutex_enter(&slot->s_evlock);
+	slot->s_errno = errno;
+	slot->s_xfrdone = B_TRUE;
+	cv_broadcast(&slot->s_evcv);
+	mutex_exit(&slot->s_evlock);
+}
+
+void
+sda_slot_detect(sda_slot_t *slot)
+{
+	mutex_enter(&slot->s_evlock);
+	slot->s_detect = B_TRUE;
+	cv_broadcast(&slot->s_evcv);
+	mutex_exit(&slot->s_evlock);
+}
+
+void
+sda_slot_fault(sda_slot_t *slot, sda_fault_t fault)
+{
+	mutex_enter(&slot->s_evlock);
+	slot->s_fault = fault;
+	cv_broadcast(&slot->s_evcv);
+	mutex_exit(&slot->s_evlock);
+}
+
+void
+sda_slot_wakeup(sda_slot_t *slot)
+{
+	mutex_enter(&slot->s_evlock);
+	slot->s_wake = B_TRUE;
+	cv_broadcast(&slot->s_evcv);
+	mutex_exit(&slot->s_evlock);
+}
+
+void
+sda_slot_init(sda_slot_t *slot)
+{
+	mutex_init(&slot->s_lock, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&slot->s_cv, NULL, CV_DRIVER, NULL);
+	mutex_init(&slot->s_evlock, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&slot->s_evcv, NULL, CV_DRIVER, NULL);
+
+	sda_cmd_list_init(&slot->s_cmdlist);
+	sda_cmd_list_init(&slot->s_abortlist);
+}
+
+void
+sda_slot_fini(sda_slot_t *slot)
+{
+	sda_cmd_list_fini(&slot->s_cmdlist);
+	sda_cmd_list_fini(&slot->s_abortlist);
+	mutex_destroy(&slot->s_lock);
+	mutex_destroy(&slot->s_evlock);
+	cv_destroy(&slot->s_cv);
+	cv_destroy(&slot->s_evcv);
+}
+
+void
+sda_slot_attach(sda_slot_t *slot)
+{
+	sda_host_t	*h = slot->s_host;
+	char		name[16];
+	kthread_t	*thr;
+	uint32_t	cap;
+
+	/*
+	 * We have both a thread and a taskq.  The taskq is used for
+	 * card initialization.
+	 *
+	 * The thread is used for the main processing loop.
+	 *
+	 * The reason for a separate taskq is that initialization
+	 * needs to acquire locks which may be held by the slot
+	 * thread, or by device driver context... use of the separate
+	 * taskq breaks the deadlock.  Additionally, the
+	 * initialization task may need to sleep quite a while during
+	 * card initialization.
+	 */
+
+	sda_slot_enter(slot);
+
+	(void) snprintf(name, sizeof (name), "slot_%d_tq", slot->s_slot_num);
+	slot->s_tq = ddi_taskq_create(h->h_dip, name, 1, TASKQ_DEFAULTPRI, 0);
+	if (slot->s_tq == NULL) {
+		/* Generally, this failure should never occur */
+		sda_slot_err(slot, "Unable to create slot taskq");
+		sda_slot_exit(slot);
+		return;
+	}
+
+	/* create the main processing thread */
+	thr = thread_create(NULL, 0, sda_slot_thread, slot, 0, &p0, TS_RUN,
+	    minclsyspri);
+	slot->s_thrid = thr->t_did;
+
+	/*
+	 * Determine slot capabilities.
+	 */
+	slot->s_caps = 0;
+
+	if ((sda_getprop(slot, SDA_PROP_CAP_NOPIO, &cap) == 0) && (cap != 0)) {
+		slot->s_caps |= SLOT_CAP_NOPIO;
+	}
+	if ((sda_getprop(slot, SDA_PROP_CAP_4BITS, &cap) == 0) && (cap != 0)) {
+		slot->s_caps |= SLOT_CAP_4BITS;
+	}
+	if ((sda_getprop(slot, SDA_PROP_CAP_HISPEED, &cap) == 0) &&
+	    (cap != 0)) {
+		slot->s_caps |= SLOT_CAP_HISPEED;
+	}
+
+	/* make sure that the host is started up */
+	if (slot->s_ops.so_reset(slot->s_prv) != 0) {
+		sda_slot_fault(slot, SDA_FAULT_RESET);
+	}
+
+	sda_slot_exit(slot);
+}
+
+void
+sda_slot_detach(sda_slot_t *slot)
+{
+	/*
+	 * Shut down the thread.
+	 */
+	if (slot->s_thrid) {
+		mutex_enter(&slot->s_evlock);
+		slot->s_detach = B_TRUE;
+		cv_broadcast(&slot->s_evcv);
+		mutex_exit(&slot->s_evlock);
+	}
+	thread_join(slot->s_thrid);
+
+	/*
+	 * Nuke the taskq. We do this after killing the
+	 * thread, to ensure that the thread doesn't try to
+	 * dispatch to it.
+	 */
+	if (slot->s_tq)
+		ddi_taskq_destroy(slot->s_tq);
+}
+
+void
+sda_slot_thread(void *arg)
+{
+	sda_slot_t	*slot = arg;
+#ifndef	__lock_lint
+	callb_cpr_t	cprinfo;
+
+	CALLB_CPR_INIT(&cprinfo, &slot->s_evlock, callb_generic_cpr,
+	    "sda_slot_thread");
+#endif
+
+	for (;;) {
+		sda_cmd_t	*cmdp;
+		boolean_t	datline;
+		sda_err_t	rv;
+
+		mutex_enter(&slot->s_evlock);
+
+		/*
+		 * Process any abort list first.
+		 */
+		if ((cmdp = list_head(&slot->s_abortlist)) != NULL) {
+			list_remove(&slot->s_abortlist, cmdp);
+			mutex_exit(&slot->s_evlock);
+			/*
+			 * EOK used here, to avoid clobbering previous
+			 * error code.
+			 */
+			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT,
+			    SDA_EOK);
+			continue;
+		}
+
+		if (slot->s_detach) {
+			/* parent is detaching the slot, bail out */
+			break;
+		}
+
+		if (slot->s_detect) {
+			slot->s_detect = B_FALSE;
+			mutex_exit(&slot->s_evlock);
+
+			sda_slot_handle_detect(slot);
+			continue;
+		}
+
+		if (slot->s_xfrdone) {
+			sda_err_t	errno;
+
+			errno = slot->s_errno;
+			slot->s_errno = SDA_EOK;
+			slot->s_xfrdone = B_FALSE;
+			mutex_exit(&slot->s_evlock);
+
+			sda_slot_handle_transfer(slot, errno);
+			continue;
+		}
+
+		if (slot->s_fault != SDA_FAULT_NONE) {
+			sda_fault_t	fault;
+
+			fault = slot->s_fault;
+			slot->s_fault = SDA_FAULT_NONE;
+			mutex_exit(&slot->s_evlock);
+
+			sda_slot_handle_fault(slot, fault);
+			continue;
+		}
+
+		if (slot->s_reap) {
+			/*
+			 * Do not sleep while holding the evlock.  If this
+			 * fails, we'll just try again the next cycle.
+			 */
+			(void) ddi_taskq_dispatch(slot->s_tq, sda_nexus_reap,
+			    slot, DDI_NOSLEEP);
+		}
+
+		if ((slot->s_xfrp != NULL) && (gethrtime() > slot->s_xfrtmo)) {
+			/*
+			 * The device stalled processing the data request.
+			 * At this point, we really have no choice but to
+			 * nuke the request, and flag a fault.
+			 */
+			mutex_exit(&slot->s_evlock);
+			sda_slot_handle_transfer(slot, SDA_ETIME);
+			sda_slot_fault(slot, SDA_FAULT_TIMEOUT);
+			continue;
+		}
+
+		if (!slot->s_wake) {
+
+			/*
+			 * We use a timed wait if we are waiting for a
+			 * data transfer to complete, or if we might
+			 * need to reap child nodes.  Otherwise we
+			 * avoid the timed wait to avoid waking CPU
+			 * (power savings.)
+			 */
+#ifndef	__lock_lint
+			CALLB_CPR_SAFE_BEGIN(&cprinfo);
+#endif
+
+			if ((slot->s_xfrp != NULL) || (slot->s_reap)) {
+				/* wait 3 sec (reap attempts) */
+
+				(void) cv_timedwait(&slot->s_evcv,
+				    &slot->s_evlock,
+				    ddi_get_lbolt() + drv_usectohz(3000000));
+			} else {
+				(void) cv_wait(&slot->s_evcv, &slot->s_evlock);
+			}
+#ifndef	__lock_lint
+			CALLB_CPR_SAFE_END(&cprinfo, &slot->s_evlock);
+#endif
+
+			mutex_exit(&slot->s_evlock);
+			continue;
+		}
+
+		slot->s_wake = B_FALSE;
+
+		/*
+		 * Possibly reap child nodes.
+		 */
+		if (slot->s_reap) {
+			slot->s_reap = B_FALSE;
+			mutex_exit(&slot->s_evlock);
+			sda_nexus_reap(slot);
+		} else {
+			mutex_exit(&slot->s_evlock);
+		}
+
+		/*
+		 * We're awake now, so look for work to do.  First
+		 * acquire access to the slot.
+		 */
+
+		sda_slot_enter(slot);
+
+		/*
+		 * If no more commands to process, go back to sleep.
+		 */
+		if ((cmdp = list_head(&slot->s_cmdlist)) == NULL) {
+			sda_slot_exit(slot);
+			continue;
+		}
+
+		datline = ((cmdp->sc_flags & SDA_CMDF_DAT) != 0);
+
+		if (datline) {
+			/*
+			 * If the current command has a data phase
+			 * while a transfer is in progress, then go
+			 * back to sleep.
+			 */
+			if (slot->s_xfrp != NULL) {
+				sda_slot_exit(slot);
+				continue;
+			}
+
+			/*
+			 * Note that APP_CMD doesn't have a data phase,
+			 * although the associated ACMD might.
+			 */
+			if (cmdp->sc_index != CMD_APP_CMD) {
+				slot->s_xfrp = cmdp;
+				/*
+				 * All commands should complete in
+				 * less than 5 seconds.  The worst
+				 * case is actually somewhere around 4
+				 * seconds, but that is when the clock
+				 * is only 100 kHz.
+				 */
+				slot->s_xfrtmo = gethrtime() +
+				    5000000000ULL;
+				(void) sda_setprop(slot, SDA_PROP_LED, 1);
+			}
+		}
+
+		/*
+		 * We're committed to dispatching this command now,
+		 * so remove it from the list.
+		 */
+		list_remove(&slot->s_cmdlist, cmdp);
+
+		/*
+		 * There could be more commands after this one, so we
+		 * mark ourself so we stay awake for another cycle.
+		 */
+		sda_slot_wakeup(slot);
+
+		/*
+		 * Submit the command.  Note that we are holding the
+		 * slot lock here, so it is critical that the caller
+		 * *not* call back up into the framework.  The caller
+		 * must break context.  But doing it this way prevents
+		 * a critical race on card removal.
+		 *
+		 * During initialization, we reject any commands that
+		 * are not from the initialization code.  This does
+		 * have the side effect of removing them.
+		 *
+		 * Note that we don't resubmit memory to the device if
+		 * it isn't flagged as ready (e.g. if the wrong device
+		 * was inserted!)
+		 */
+		if (((!slot->s_ready) && (cmdp->sc_flags & SDA_CMDF_MEM)) ||
+		    (slot->s_init && !(cmdp->sc_flags & SDA_CMDF_INIT))) {
+			rv = SDA_ENODEV;
+			if (!slot->s_warn) {
+				sda_slot_err(slot,
+				    "Device removed while in use.  "
+				    "Please reinsert!");
+				slot->s_warn = B_TRUE;
+			}
+		} else {
+			rv = slot->s_ops.so_cmd(slot->s_prv, cmdp);
+		}
+		if (rv == SDA_EOK)
+			rv = sda_slot_check_response(cmdp);
+
+		if (rv == SDA_EOK) {
+			/*
+			 * If APP_CMD completed properly, then
+			 * resubmit with ACMD index.  Note wake was
+			 * already set above.
+			 */
+			if (cmdp->sc_index == CMD_APP_CMD) {
+				if ((cmdp->sc_response[0] & R1_APP_CMD) == 0) {
+					sda_slot_log(slot, "APP_CMD not set!");
+				}
+				sda_cmd_resubmit_acmd(slot, cmdp);
+				sda_slot_exit(slot);
+
+				continue;
+			}
+
+		} else if (datline) {
+			/*
+			 * If an error occurred and we were expecting
+			 * a transfer phase, we have to clean up.
+			 */
+			(void) sda_setprop(slot, SDA_PROP_LED, 0);
+			slot->s_xfrp = NULL;
+			slot->s_xfrtmo = 0;
+
+			/*
+			 * And notify any waiter.
+			 */
+			sda_slot_exit(slot);
+			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT, rv);
+			continue;
+		}
+
+		/*
+		 * Wake any waiter.
+		 */
+		sda_slot_exit(slot);
+		sda_cmd_notify(cmdp, SDA_CMDF_BUSY, rv);
+	}
+
+#ifdef	__lock_lint
+	mutex_exit(&slot->s_evlock);
+#else
+	CALLB_CPR_EXIT(&cprinfo);
+#endif
+
+	thread_exit();
+}
+
+void
+sda_slot_vprintf(sda_slot_t *s, int level, const char *fmt, va_list ap)
+{
+	char		msgbuf[256];
+	const char	*pfx, *sfx;
+
+	if (level == CE_CONT) {
+		pfx = "!";
+		sfx = "\n";
+	} else {
+		pfx = sfx = "";
+	}
+
+	if (s != NULL) {
+		dev_info_t	*dip = s->s_host->h_dip;
+
+		(void) snprintf(msgbuf, sizeof (msgbuf),
+		    "%s%s%d: slot %d: %s%s", pfx,
+		    ddi_driver_name(dip), ddi_get_instance(dip),
+		    s->s_slot_num, fmt, sfx);
+	} else {
+		(void) snprintf(msgbuf, sizeof (msgbuf), "%ssda: %s%s",
+		    pfx, fmt, sfx);
+	}
+	vcmn_err(level, msgbuf, ap);
+}
+
+void
+sda_slot_err(sda_slot_t *s, const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	sda_slot_vprintf(s, CE_WARN, fmt, ap);
+	va_end(ap);
+}
+
+void
+sda_slot_log(sda_slot_t *s, const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	sda_slot_vprintf(s, CE_CONT, fmt, ap);
+	va_end(ap);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,105 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SD memory card target driver.  It relies on the SDA common
+ * framework, and translates to SCSA.  That is to say, it emulates a
+ * simple SCSI block device.
+ *
+ * The entire driver is a tiny shim for the SDA framework, because to
+ * make life simplify and reduce layering overhead, we just use implementation
+ * in the SDA framework.
+ *
+ * (We have to be a separate driver, unfortunately, because SDA nexus drivers
+ * need to support SDIO and memory targets, and there can only be one bus_ops
+ * per driver.)
+ */
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+
+/* our entire API with SDA is miniscule */
+extern void sda_mem_init(struct modlinkage *);
+extern void sda_mem_fini(struct modlinkage *);
+
+static struct dev_ops sdcard_devops = {
+	DEVO_REV,
+	0,
+	NULL,
+	nulldev,
+	nulldev,
+	NULL,
+	NULL,
+	nodev,
+	NULL,	/* cb_ops */
+	NULL,	/* bus_ops */
+	NULL,	/* power */
+};
+
+static struct modldrv modldrv = {
+	&mod_driverops,
+	"SD Memory Slot",
+	&sdcard_devops,
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, { &modldrv, NULL }
+};
+
+int
+_init(void)
+{
+	int	rv;
+
+	sda_mem_init(&modlinkage);
+
+	if ((rv = mod_install(&modlinkage)) != 0) {
+		sda_mem_fini(&modlinkage);
+		return (rv);
+	}
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int	rv;
+
+	if ((rv = mod_remove(&modlinkage)) == 0) {
+		sda_mem_fini(&modlinkage);
+		return (rv);
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/warlock/blk2scsa.wlcmd	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,63 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+root	b2s_mod_fini
+root	b2s_mod_init
+root	b2s_alloc_nexus
+root	b2s_attach_leaf
+root	b2s_attach_nexus
+root	b2s_detach_leaf
+root	b2s_detach_nexus
+root	b2s_free_nexus
+root	b2s_request_dma
+root	b2s_tran_tgt_free
+root	b2s_tran_tgt_init
+
+root	b2s_tran_setup_pkt
+root	b2s_tran_start
+root	b2s_tran_teardown_pkt
+root	b2s_tran_tgt_free
+root	b2s_tran_tgt_init
+
+add	bus_ops::bus_config		targets	b2s_bus_config
+
+add 	scsi_pkt::pkt_comp		targets \
+					b2s_tran_teardown_pkt \
+					b2s_tran_setup_pkt \
+					b2s_tran_abort \
+					b2s_tran_reset \
+					b2s_tran_getcap \
+					b2s_tran_setcap \
+					b2s_tran_start
+
+add	b2s_nexus::n_request		targets	b2s_request_dma
+
+add	bus_ops::bus_add_eventcall	targets	warlock_dummy
+add	bus_ops::bus_unconfig		targets	warlock_dummy
+add	bus_ops::bus_get_eventcookie	targets warlock_dummy
+add	bus_ops::bus_intr_ctl		targets	warlock_dummy
+add	bus_ops::bus_post_event		targets warlock_dummy
+add	bus_ops::bus_remove_eventcall	targets warlock_dummy
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/warlock/sdhost.wlcmd	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+root	sda_host_log
+root	sdhost_poll
+
+add sda_slot::s_ops.so_cmd	targets sdhost_cmd \
+					sdhost_halt
+add sda_slot::s_ops.so_getprop	targets sdhost_getprop
+add sda_slot::s_ops.so_reset	targets sdhost_reset
+add sda_slot::s_ops.so_setprop	targets sdhost_setprop
+
--- a/usr/src/uts/common/sys/Makefile	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/common/sys/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -22,8 +22,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 include $(SRC)/uts/Makefile.uts
 
@@ -859,6 +857,9 @@
 	sddef.h		\
 	smp.h
 
+SCSIADHDRS=		\
+	blk2scsa.h
+
 SCSICADHDRS=
 
 SCSIVHCIHDRS=		\
@@ -866,6 +867,11 @@
 	mpapi_impl.h	\
 	mpapi_scsi_vhci.h
 
+SDCARDHDRS=		\
+	sda.h		\
+	sda_impl.h	\
+	sda_ioctl.h
+
 FCHDRS=			\
 	fc_transport.h	\
 	linkapp.h	\
@@ -1049,12 +1055,14 @@
 	$(LVMHDRS:%.h=lvm/%.check)			\
 	$(PCMCIAHDRS:%.h=pcmcia/%.check)		\
 	$(SCSIHDRS:%.h=scsi/%.check)			\
-	$(SCSICONHDRS:%.h=scsi/conf/%.check)		\
+	$(SCSIADHDRS:%.h=scsi/adapters/%.check)		\
+	$(SCSICONFHDRS:%.h=scsi/conf/%.check)		\
 	$(SCSIIMPLHDRS:%.h=scsi/impl/%.check)		\
 	$(SCSITARGETSHDRS:%.h=scsi/targets/%.check)	\
 	$(SCSIVHCIHDRS:%.h=scsi/adapters/%.check)	\
 	$(FCHDRS:%.h=fc4/%.check)			\
 	$(SATAGENHDRS:%.h=sata/%.check)			\
+	$(SDCARDHDRS:%.h=sdcard/%.check)		\
 	$(SYSEVENTHDRS:%.h=sysevent/%.check)            \
 	$(CONTRACTHDRS:%.h=contract/%.check)            \
 	$(USBAUDHDRS:%.h=usb/clients/audio/%.check)   \
@@ -1096,11 +1104,13 @@
 	$(ROOTLVMHDRS)		\
 	$(ROOTPCMCIAHDRS)	\
 	$(ROOTSCSIHDRS)		\
+	$(ROOTSCSIADHDRS)	\
 	$(ROOTSCSICONFHDRS)	\
 	$(ROOTSCSIGENHDRS)	\
 	$(ROOTSCSIIMPLHDRS)	\
 	$(ROOTSCSIVHCIHDRS)	\
 	$(ROOTFCHDRS)		\
+	$(ROOTSDCARDHDRS)	\
 	$(ROOTSYSEVENTHDRS)     \
 	$(ROOTCONTRACTHDRS)     \
 	$(ROOTUSBHDRS)		\
@@ -1144,11 +1154,13 @@
 	$(ROOTLVMHDRS)		\
 	$(ROOTPCMCIAHDRS)	\
 	$(ROOTSCSIHDRS)		\
+	$(ROOTSCSIADHDRS)	\
 	$(ROOTSCSICONFHDRS)	\
 	$(ROOTSCSIGENHDRS)	\
 	$(ROOTSCSIIMPLHDRS)	\
 	$(ROOTSCSIVHCIHDRS)	\
 	$(ROOTFCHDRS)		\
+	$(ROOTSDCARDHDRS)	\
 	$(ROOTSYSEVENTHDRS)     \
 	$(ROOTCONTRACTHDRS)     \
 	$(ROOTUSBHDRS)		\
--- a/usr/src/uts/common/sys/Makefile.syshdrs	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/common/sys/Makefile.syshdrs	Fri Aug 08 11:50:24 2008 -0700
@@ -18,11 +18,9 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 # Common definitions for open and closed headers.
 
@@ -97,6 +95,9 @@
 scsi/adapters/%.check:	scsi/adapters/%.h
 	$(DOT_H_CHECK)
 
+sdcard/%.check:		sdcard/%.h
+	$(DOT_H_CHECK)
+
 sysevent/%.check:     sysevent/%.h
 	$(DOT_H_CHECK)
 
@@ -178,6 +179,7 @@
 	$(ROOTDIR)/scsi/generic	\
 	$(ROOTDIR)/scsi/impl	\
 	$(ROOTDIR)/fc4		\
+	$(ROOTDIR)/sdcard	\
 	$(ROOTDIR)/sysevent     \
 	$(ROOTDIR)/contract     \
 	$(ROOTDIR)/usb		\
@@ -237,10 +239,13 @@
 ROOTSCSIIMPLHDRS= $(SCSIIMPLHDRS:%=$(ROOTDIR)/scsi/impl/%)
 ROOTSCSITARGETSHDRS= $(SCSITARGETSHDRS:%=$(ROOTDIR)/scsi/targets/%)
 ROOTSCSICADHDRS= $(SCSICADHDRS:%=$(ROOTDIR)/scsi/adapters/%)
+ROOTSCSIADHDRS= $(SCSICADHDRS:%=$(ROOTDIR)/scsi/adapters/%)
 ROOTSCSIVHCIHDRS= $(SCSIVHCIHDRS:%=$(ROOTDIR)/scsi/adapters/%)
 
 ROOTFCHDRS= $(FCHDRS:%=$(ROOTDIR)/fc4/%)
 
+ROOTSDCARDHDRS= $(SDCARDHDRS:%=$(ROOTDIR)/sdcard/%)
+
 ROOTSYSEVENTHDRS= $(SYSEVENTHDRS:%=$(ROOTDIR)/sysevent/%)
 ROOTCONTRACTHDRS= $(CONTRACTHDRS:%=$(ROOTDIR)/contract/%)
 
@@ -346,6 +351,9 @@
 $(ROOTDIR)/fc4/%:		fc4/%
 	$(INS.file)
 
+$(ROOTDIR)/sdcard/%:		sdcard/%
+	$(INS.file)
+
 $(ROOTDIR)/sysevent/%:		sysevent/%
 	$(INS.file)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/blk2scsa.h	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,182 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SCSI_ADAPTERS_BLK2SCSA_H
+#define	_SYS_SCSI_ADAPTERS_BLK2SCSA_H
+
+#include <sys/types.h>
+#include <sys/ksynch.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct b2s_nexus_info b2s_nexus_info_t;
+typedef struct b2s_leaf_info b2s_leaf_info_t;
+typedef struct b2s_media b2s_media_t;
+typedef struct b2s_inquiry b2s_inquiry_t;
+typedef struct b2s_request b2s_request_t;
+typedef struct b2s_nexus b2s_nexus_t;
+typedef struct b2s_leaf b2s_leaf_t;
+
+struct b2s_media {
+	uint64_t	media_blksz;
+	uint64_t	media_nblks;
+	uint64_t	media_flags;
+};
+#define	B2S_MEDIA_FLAG_READ_ONLY	(1U << 1)
+#define	B2S_MEDIA_FLAG_LOCKED		(1U << 2)
+
+
+struct b2s_inquiry {
+	const char	*inq_vendor;
+	const char	*inq_product;
+	const char	*inq_revision;
+	const char	*inq_serial;
+};
+
+struct b2s_nexus_info {
+	int		nexus_version;
+	dev_info_t	*nexus_dip;
+	void		*nexus_private;
+	ddi_dma_attr_t	*nexus_dma_attr;
+	boolean_t	(*nexus_request)(void *, b2s_request_t *);
+};
+
+struct b2s_leaf_info {
+	uint_t		leaf_target;
+	uint_t		leaf_lun;
+	uint32_t	leaf_flags;
+	const char	*leaf_unique_id;
+};
+
+#define	B2S_LEAF_REMOVABLE	(1U << 0)
+#define	B2S_LEAF_HOTPLUGGABLE	(1U << 1)
+/* these values reserved! */
+#define	B2S_LEAF_DETACHED	(1U << 16)
+
+typedef enum {
+	B2S_CMD_GETMEDIA = 0,	/* get content */
+	B2S_CMD_FORMAT = 1,	/* format media */
+	B2S_CMD_START = 2,	/* spin up */
+	B2S_CMD_STOP = 3,	/* spin down */
+	B2S_CMD_LOCK = 4,	/* lock media door */
+	B2S_CMD_UNLOCK = 5,	/* unlock media door */
+	B2S_CMD_READ = 6,	/* read blocks */
+	B2S_CMD_WRITE = 7,	/* write blocks */
+	B2S_CMD_SYNC = 8,	/* flush write cache */
+	B2S_CMD_INQUIRY = 9,	/* inquiry data */
+	B2S_CMD_RESET = 10,	/* reset of bus */
+	B2S_CMD_ABORT = 11,	/* abort inflight commands */
+} b2s_cmd_t;
+
+typedef enum {
+	B2S_EOK = 0,		/* success */
+	B2S_ENOTSUP = 1,	/* operation not sup */
+	B2S_EFORMATTING = 2,	/* busy formatting */
+	B2S_ENOMEDIA = 3,	/* media not mounted */
+	B2S_EMEDIACHG = 4,	/* media changed */
+	B2S_ESTOPPED = 5,	/* unit not started */
+	B2S_EBLKADDR = 6,	/* blkno invalid */
+	B2S_EIO = 7,		/* general failure */
+	B2S_EHARDWARE = 8,	/* hardware error */
+	B2S_ENODEV = 9,		/* hardware removed */
+	B2S_EMEDIA = 10,	/* media problem */
+	B2S_EDOORLOCK = 11,	/* door lock engaged */
+	B2S_EWPROTECT = 12,	/* write protected */
+	B2S_ESTARTING = 13,	/* unit spinning up */
+	B2S_ETIMEDOUT = 14,	/* request timed out */
+	B2S_ENOMEM = 15,	/* out of memory */
+	B2S_ERESET = 16,	/* reset aborted command */
+	B2S_EABORT = 17,	/* aborted command */
+
+	/* these are framework internal use only */
+	B2S_ERSVD = 18,		/* unit reserved */
+	B2S_EINVAL = 19,	/* invalid parameter */
+	B2S_EPARAM = 20,	/* bad parameter */
+	B2S_EBADMSG = 21,	/* malformed message */
+	B2S_ENOSAV = 22,	/* no saveable parms */
+
+	/* used internally for array sizing, must be last */
+	B2S_NERRS = 23
+} b2s_err_t;
+
+#define	B2S_REQUEST_FLAG_POLL		(1U << 0)	/* use polled io */
+#define	B2S_REQUEST_FLAG_HEAD		(1U << 1)
+#define	B2S_REQUEST_FLAG_DONE		(1U << 2)
+#define	B2S_REQUEST_FLAG_LOAD_EJECT	(1U << 3)	/* for start/stop */
+#define	B2S_REQUEST_FLAG_IMMED		(1U << 4)	/* get status immed */
+/* framework internal flags */
+#define	B2S_REQUEST_FLAG_BLKS		(1U << 16)	/* block-oriented */
+#define	B2S_REQUEST_FLAG_MAPIN		(1U << 17)	/* bp_mapin done */
+
+struct b2s_request {
+	b2s_cmd_t		br_cmd;
+	b2s_err_t		br_errno;
+	uint_t			br_target;
+	uint_t			br_lun;
+	uint32_t		br_flags;
+
+	/* note that this member should come last for future expansion */
+	union {
+		uint64_t	a_ints[3];
+		b2s_media_t	a_media;
+		b2s_inquiry_t	a_inquiry;
+	} br_args;
+};
+#define	br_lba			br_args.a_ints[0]
+#define	br_nblks		br_args.a_ints[1]
+#define	br_media		br_args.a_media
+#define	br_inquiry		br_args.a_inquiry
+
+
+int b2s_mod_init(struct modlinkage *);
+void b2s_mod_fini(struct modlinkage *);
+
+/* used as version to alloc_hba */
+#define	B2S_VERSION_0	0
+
+b2s_nexus_t *b2s_alloc_nexus(b2s_nexus_info_t *);
+void b2s_free_nexus(b2s_nexus_t *);
+int b2s_attach_nexus(b2s_nexus_t *);
+int b2s_detach_nexus(b2s_nexus_t *);
+
+b2s_leaf_t *b2s_attach_leaf(b2s_nexus_t *, b2s_leaf_info_t *);
+void b2s_detach_leaf(b2s_leaf_t *);
+
+/*
+ * Address information.
+ */
+void b2s_request_mapin(b2s_request_t *, caddr_t *, size_t *);
+void b2s_request_dma(b2s_request_t *, uint_t *, ddi_dma_cookie_t **);
+void b2s_request_done(b2s_request_t *, b2s_err_t, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _SYS_SCSI_ADAPTERS_BLK2SCSA_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/sdcard/sda.h	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,327 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SDCARD_SDA_H
+#define	_SYS_SDCARD_SDA_H
+
+#include <sys/types.h>
+#include <sys/note.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * SD card common framework.  This module provides most of the common
+ * functionality so that SecureDigital host adapters and client devices
+ * (such as the sdmem driver) can share common code.
+ */
+
+/*
+ * SD Commands.  Commmand format is 48-bits as follows:
+ *
+ * bits		value		desc
+ * -------------------------------------------
+ * 47		0		start bit
+ * 46		1		transmission bit
+ * 45:40	cmd		command index (see values listed below)
+ * 39:8		arg		32-bit command argument
+ * 7:1		crc7		crc7 check value
+ * 0		1		end bit
+ * -------------------------------------------
+ */
+typedef enum {
+	CMD_GO_IDLE = 0,
+	CMD_SEND_OCR = 1,		/* MMC only */
+	CMD_BCAST_CID = 2,
+	CMD_SEND_RCA = 3,
+	CMD_SET_DSR = 4,
+	CMD_IO_SEND_OCR = 5,		/* SDIO only */
+	CMD_SWITCH_FUNC = 6,
+	CMD_SELECT_CARD = 7,
+	CMD_SEND_IF_COND = 8,
+	CMD_SEND_CSD = 9,
+	CMD_SEND_CID = 10,
+	CMD_STOP_TRANSMIT = 12,
+	CMD_SEND_STATUS = 13,
+	CMD_GO_INACTIVE = 15,
+	CMD_SET_BLOCKLEN = 16,
+	CMD_READ_SINGLE = 17,
+	CMD_READ_MULTI = 18,
+	CMD_WRITE_SINGLE = 24,
+	CMD_WRITE_MULTI = 25,
+	CMD_PROGRAM_CSD = 27,
+	CMD_SET_WRITE_PROT = 28,
+	CMD_CLR_WRITE_PROT = 29,
+	CMD_SEND_WRITE_PROT = 30,
+	CMD_ERASE_START = 32,
+	CMD_ERASE_END = 33,
+	CMD_ERASE = 38,
+	CMD_LOCK = 42,
+	CMD_IO_RW_DIRECT = 52,
+	CMD_IO_RW_EXTENDED = 53,
+	CMD_APP_CMD = 55,
+	CMD_GEN_CMD = 56,
+	/* APP CMD values, send ACMD first */
+	ACMD_SET_BUS_WIDTH = 6,
+	ACMD_SD_STATUS = 13,
+	ACMD_SEND_NUM_WR_BLKS = 22,
+	ACMD_SET_WR_BLK_ERASE_COUNT = 23,
+	ACMD_SD_SEND_OCR = 41,
+	ACMD_SET_CLR_CARD_DETECT = 42,
+	ACMD_SEND_SCR = 51
+} sda_index_t;
+
+/*
+ * Classes of response type.  Note that we encode the "busy bit" as
+ * value 0x10.
+ */
+typedef enum {
+	R0 = 0,
+	R1 = 1,
+	R2 = 2,
+	R3 = 3,
+	R4 = 4,
+	R5 = 5,
+	R6 = 6,
+	R7 = 7,
+	Rb = 0x10,
+	R1b = 0x11,
+	R5b = 0x15
+} sda_rtype_t;
+
+/*
+ * R1 status bits.
+ */
+#define	R1_OUT_OF_RANGE		(1U << 31)
+#define	R1_ADDRESS_ERROR	(1U << 30)
+#define	R1_BLOCK_LEN_ERROR	(1U << 29)
+#define	R1_ERASE_SEQ_ERROR	(1U << 28)
+#define	R1_ERASE_PARAM		(1U << 27)
+#define	R1_WP_VIOLATION		(1U << 26)
+#define	R1_CARD_IS_LOCKED	(1U << 25)
+#define	R1_LOCK_FAILED		(1U << 24)
+#define	R1_COM_CRC_ERROR	(1U << 23)
+#define	R1_ILLEGAL_COMMAND	(1U << 22)
+#define	R1_CARD_ECC_FAILED	(1U << 21)
+#define	R1_CC_ERROR		(1U << 20)
+#define	R1_ERROR		(1U << 19)
+#define	R1_CSD_OVERWRITE	(1U << 16)
+#define	R1_WP_ERASE_SKIP	(1U << 15)
+#define	R1_CARD_ECC_DIS		(1U << 14)
+#define	R1_ERASE_RESET		(1U << 13)
+#define	R1_READY_FOR_DATA	(1U << 8)
+#define	R1_APP_CMD		(1U << 5)
+#define	R1_AKE_SEQ_ERROR	(1U << 3)
+
+/*
+ * Note that R1_COM_CRC_ERR, R1_ILLEGAL_COMMAND, R1_ERASE_SEQ_ERROR, and
+ * R1_AKE_SEQ_ERROR errors are delayed error bits reported on the next
+ * command.  So we don't list them here.
+ */
+#define	R1_ERRS	(\
+	R1_ERROR | R1_OUT_OF_RANGE | R1_ADDRESS_ERROR | R1_BLOCK_LEN_ERROR | \
+	R1_ERASE_PARAM | R1_WP_VIOLATION | R1_LOCK_FAILED | \
+	R1_CARD_ECC_FAILED | R1_CC_ERROR | R1_CSD_OVERWRITE | \
+	R1_WP_ERASE_SKIP)
+
+#define	R1_STATE(x)	(((x) & 0xf) >> 9)
+
+/*
+ * R5 status bits.
+ */
+#define	R5_COM_CRC_ERROR	(1U << 7)
+#define	R5_ILLEGAL_COMMAND	(1U << 6)
+#define	R5_ERROR		(1U << 3)
+#define	R5_RFU			(1U << 2)
+#define	R5_FUNCTION_NUMBER	(1U << 1)
+#define	R5_OUT_OF_RANGE		(1U << 0)
+
+#define	R5_ERRS	(R5_ERROR | R5_FUNCTION_NUMBER | R5_OUT_OF_RANGE)
+
+#define	R5_IO_STATE(x)	(((x) & 0x3) >> 4)
+
+/*
+ * R7 bits (CMD8).
+ */
+#define	R7_VHS_27_36V		(1U << 8)
+#define	R7_PATTERN		(0xAA)
+
+/*
+ * OCR bits.
+ */
+#define	OCR_POWER_UP		(1U << 31)
+#define	OCR_CCS			(1U << 30)
+#define	OCR_FUNCS(x)		(((x) & 7) >> 28)	/* SDIO only */
+#define	OCR_MEM_PRESENT		(1U << 27)		/* SDIO only */
+#define	OCR_VOLTAGE_MASK	(0xffffffU)		/* (bits 0-23 */
+#define	OCR_HI_MASK		(0xff8000U)		/* 2.7-3.6V */
+#define	OCR_35_36V		(1U << 23)
+#define	OCR_34_35V		(1U << 22)
+#define	OCR_33_34V		(1U << 21)
+#define	OCR_32_33V		(1U << 20)
+#define	OCR_31_32V		(1U << 19)
+#define	OCR_30_31V		(1U << 18)
+#define	OCR_29_30V		(1U << 17)
+#define	OCR_28_29V		(1U << 16)
+#define	OCR_27_28V		(1U << 15)
+#define	OCR_26_27V		(1U << 14)
+#define	OCR_25_26V		(1U << 14)
+#define	OCR_24_25V		(1U << 13)
+#define	OCR_23_24V		(1U << 12)
+#define	OCR_22_23V		(1U << 11)
+#define	OCR_21_22V		(1U << 10)
+#define	OCR_20_21V		(1U << 9)
+#define	OCR_19_20V		(1U << 8)
+#define	OCR_18_19V		(1U << 7)
+#define	OCR_17_18V		(1U << 6)
+
+
+/*
+ * Command structure.  Used internally by the framework, and by host
+ * drivers.  Note that it is forbidden to depend on the size of this
+ * structure.
+ */
+typedef struct sda_cmd sda_cmd_t;
+
+struct sda_cmd {
+	/*
+	 * The ordering of these is done to maximize packing.
+	 */
+	sda_index_t		sc_index;	/* command name */
+	sda_rtype_t		sc_rtype;	/* response type expected */
+	uint16_t		sc_flags;
+	uint32_t		sc_argument;	/* command argument */
+
+	uint32_t		sc_response[4];
+
+	uint16_t		sc_nblks;
+	uint16_t		sc_blksz;
+
+	uint32_t		sc_resid;
+
+	uint_t			sc_ndmac;	/* # DMA cookies */
+	ddi_dma_cookie_t	*sc_dmacs;	/* actual DMA cookies */
+	caddr_t			sc_kvaddr;	/* kernel virtual address */
+
+#define	SDA_CMDF_READ		0x0001		/* transfer direction */
+#define	SDA_CMDF_WRITE		0x0002		/* transfer direction */
+#define	SDA_CMDF_AUTO_CMD12	0x0004		/* cmd12 requested */
+/* private flags .. not for driver consumption */
+#define	SDA_CMDF_DAT		0x0100		/* data phase pending */
+#define	SDA_CMDF_BUSY		0x0200		/* cmd in-flight or queued */
+#define	SDA_CMDF_INIT		0x0400		/* initialization command */
+#define	SDA_CMDF_MEM		0x0800		/* memory target command */
+};
+
+_NOTE(SCHEME_PROTECTS_DATA("unshared request", sda_cmd))
+
+/*
+ * The framework has two APIs.  The first API is for host controllers,
+ * and is referred to as SDHOST.  The second API is for target devices,
+ * and is referred to as SDCLIENT.  Please don't mix and match usage outside
+ * of the framework implementation itself!
+ */
+
+typedef struct sda_host sda_host_t;
+
+typedef enum {
+	SDA_PROP_INSERTED = 	1,	/* R: is card inserted? */
+	SDA_PROP_WPROTECT =	2,	/* R: is card write protected */
+	SDA_PROP_LED =		3,	/* W: LED */
+	SDA_PROP_CLOCK =	4,	/* R: frequency, Hz */
+	SDA_PROP_BUSWIDTH =	5,	/* W: bus width */
+	SDA_PROP_OCR =		6,	/* RW: ocr R: supported, W: set curr */
+	SDA_PROP_CAP_4BITS =	7,	/* R: 4 bit data bus? */
+	SDA_PROP_CAP_8BITS =	8,	/* R: MMC future expansion */
+	SDA_PROP_CAP_HISPEED =	9,	/* R: fast bus rates (> 25MHz) */
+	SDA_PROP_CAP_INTR =	10,	/* R: SDIO interrupt support */
+	SDA_PROP_CAP_NOPIO =	11,	/* R: Never needs bp_mapin */
+	SDA_PROP_HISPEED =	12	/* W: high speed (>25MHz) */
+} sda_prop_t;
+
+typedef enum {
+	SDA_FAULT_NONE =	0,	/* No failure */
+	SDA_FAULT_ACMD12 =	1,	/* Auto CMD12 failure */
+	SDA_FAULT_CRC7 =	2,	/* CRC7 failure on CMD/DAT line */
+	SDA_FAULT_PROTO =	3,	/* SD/MMC protocol error */
+	SDA_FAULT_CURRENT =	4,	/* Current overlimit detected */
+	SDA_FAULT_INIT =	5,	/* Card initialization failure */
+	SDA_FAULT_TIMEOUT =	6,	/* Unexpected timeout failure */
+	SDA_FAULT_HOST =	7,	/* Internal host or slot failure */
+	SDA_FAULT_RESET =	8,	/* Slot failed to reset */
+} sda_fault_t;
+
+typedef enum {
+	SDA_EOK =		0,	/* Success */
+	SDA_ECRC7 =		1,	/* CRC7 failure */
+	SDA_EPROTO =		2,	/* SD/MMC protocol error */
+	SDA_EINVAL =		3,	/* Invalid argument */
+	SDA_ETIME =		4,	/* Timeout */
+	SDA_ECMD12 =		5,	/* Failed during stop cmd */
+	SDA_ENOTSUP =		6,	/* Setting/property not supported */
+	SDA_ERESID =		7,	/* Incomplete transfer */
+	SDA_EFAULT =		8,	/* Previous fault condition present */
+	SDA_ENOMEM =		9,	/* Memory exhausted */
+	SDA_EWPROTECT =		10,	/* Media is write protected */
+	SDA_ENODEV =		11,	/* Card removed */
+	SDA_ERESET =		12,	/* Memory card reset */
+	SDA_EABORT =		13,	/* Memory command aborted */
+	SDA_EIO =		14,	/* Other generic error */
+	SDA_ESUSPENDED =	15,	/* Slot has been suspended */
+} sda_err_t;
+
+typedef struct sda_ops {
+	int	so_version;
+#define	SDA_OPS_VERSION	1
+	sda_err_t	(*so_cmd)(void *, sda_cmd_t *);
+	sda_err_t	(*so_getprop)(void *, sda_prop_t, uint32_t *);
+	sda_err_t	(*so_setprop)(void *, sda_prop_t, uint32_t);
+	sda_err_t	(*so_poll)(void *);
+	sda_err_t	(*so_reset)(void *);
+	sda_err_t	(*so_halt)(void *);
+} sda_ops_t;
+
+/*
+ * Host operations.
+ */
+void sda_host_init_ops(struct dev_ops *);
+void sda_host_fini_ops(struct dev_ops *);
+sda_host_t *sda_host_alloc(dev_info_t *, int, sda_ops_t *, ddi_dma_attr_t *);
+void sda_host_free(sda_host_t *);
+void sda_host_set_private(sda_host_t *, int, void *);
+int sda_host_attach(sda_host_t *);
+void sda_host_detach(sda_host_t *);
+void sda_host_detect(sda_host_t *, int);
+void sda_host_fault(sda_host_t *, int, sda_fault_t);
+void sda_host_transfer(sda_host_t *, int, sda_err_t);
+/*PRINTFLIKE3*/
+void sda_host_log(sda_host_t *, int, const char *, ...);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_SDCARD_SDA_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/sdcard/sda_impl.h	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,283 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SDCARD_SDA_IMPL_H
+#define	_SYS_SDCARD_SDA_IMPL_H
+
+#include <sys/list.h>
+#include <sys/ksynch.h>
+#include <sys/note.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sdcard/sda.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Type and structure definitions.
+ */
+typedef struct sda_slot sda_slot_t;
+
+/*
+ * Per slot state.
+ */
+struct sda_slot {
+	sda_host_t	*s_host;
+	void		*s_prv;			/* bus private data */
+
+	int		s_slot_num;
+	boolean_t	s_inserted;
+	boolean_t	s_failed;
+	uint8_t		s_num_io;
+	uint32_t	s_cur_ocr;		/* current ocr */
+
+	uint16_t	s_rca;
+	uint32_t	s_maxclk;		/* maximum freq for card */
+
+	sda_cmd_t	*s_xfrp;		/* pending transfer cmd */
+	hrtime_t	s_xfrtmo;		/* transfer timeout */
+
+	boolean_t	s_reap;
+	boolean_t	s_warn;
+	boolean_t	s_ready;		/* target node ready */
+	boolean_t	s_init;			/* slot initializing */
+
+	/* these are protected by the evlock */
+	boolean_t	s_wake;			/* wake up thread */
+	boolean_t	s_detach;		/* detach in progress */
+	boolean_t	s_detect;		/* detect event occurred */
+	sda_fault_t	s_fault;
+	boolean_t	s_xfrdone;		/* transfer event occurred */
+	sda_err_t	s_errno;
+
+	uint16_t	s_flags;
+#define	SLOTF_WRITABLE		0x0004
+#define	SLOTF_4BITS		0x0008
+#define	SLOTF_IFCOND		0x0010
+#define	SLOTF_MMC		0x0020
+#define	SLOTF_SDMEM		0x0040
+#define	SLOTF_SDIO		0x0080
+#define	SLOTF_SDHC		0x0100
+#define	SLOTF_MEMORY		(SLOTF_MMC | SLOTF_SDMEM)
+#define	SLOTF_SD		(SLOTF_SDMEM | SLOTF_SDIO)
+
+	uint16_t	s_caps;
+#define	SLOT_CAP_NOPIO		0x0002
+#define	SLOT_CAP_HISPEED	0x0004
+#define	SLOT_CAP_4BITS		0x0008
+
+	list_t		s_cmdlist;
+	list_t		s_abortlist;
+
+	/*
+	 * Slot operations.  Slot local copy for performance.
+	 */
+	sda_ops_t	s_ops;
+
+	/*
+	 * Recursive locking of slot.
+	 */
+	kmutex_t	s_lock;
+	kcondvar_t	s_cv;
+	kt_did_t	s_owner;	/* owner holding the slot */
+	uint32_t	s_circular;	/* circular sda_slot_enter() calls */
+
+	/*
+	 * Event notification/thread wakeup.
+	 */
+	kmutex_t	s_evlock;
+	kcondvar_t	s_evcv;
+
+	/*
+	 * Asynch. threads.
+	 */
+	kt_did_t	s_thrid;	/* processing thread id */
+	ddi_taskq_t	*s_tq;		/* insert taskq */
+
+	/*
+	 * Timestamping for cfgadm benefit.
+	 */
+	uint8_t		s_intransit;
+	time_t		s_stamp;
+
+	/*
+	 * Memory card-specific.
+	 */
+	uint32_t	s_rcsd[4];	/* raw csd */
+	uint32_t	s_rcid[4];	/* raw cid */
+	uint32_t	s_nblks;	/* total blocks on device */
+	uint16_t	s_blksz;	/* device block size (typ. 512) */
+	uint16_t	s_bshift;	/* block address shift factor */
+	uint32_t	s_speed;	/* max memory clock in hz */
+
+	/* Other CID and CSD values */
+	uint32_t	s_mfg;		/* mfg id */
+	char		s_prod[8];	/* product id */
+	char		s_oem[2];	/* oem id */
+	uint32_t	s_serial;
+	uint8_t		s_majver;
+	uint8_t		s_minver;
+	uint16_t	s_year;
+	uint8_t		s_month;
+
+	uint16_t	s_ccc;		/* card command classes */
+	uint8_t		s_r2w;		/* read/write factor */
+	uint8_t		s_dsr;		/* DSR implemented? */
+	uint8_t		s_perm_wp;	/* permanent write protect set? */
+	uint8_t		s_temp_wp;	/* temporary write protect set? */
+
+	char		s_uuid[40];	/* fabricated universal unique id */
+
+	struct b2s_nexus	*s_nexus;
+	struct b2s_leaf		*s_leaf;
+};
+
+_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_lock, sda_slot::s_circular))
+_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_wake))
+_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_detach))
+_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_detect))
+_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_fault))
+_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_xfrdone))
+_NOTE(MUTEX_PROTECTS_DATA(sda_slot::s_evlock, sda_slot::s_errno))
+_NOTE(SCHEME_PROTECTS_DATA("slot_enter", sda_slot::s_warn))
+_NOTE(SCHEME_PROTECTS_DATA("slot_enter", sda_slot::s_xfrtmo))
+_NOTE(SCHEME_PROTECTS_DATA("slot_enter", sda_slot::s_xfrp))
+
+/*
+ * Per host state.  One per devinfo node.  There could be multiple
+ * slots per devinfo node.
+ */
+struct sda_host {
+	dev_info_t	*h_dip;
+	int		h_nslot;
+	sda_slot_t	*h_slots;
+	ddi_dma_attr_t	*h_dma;		/* dma attr, needed for mem */
+
+	list_node_t	h_node;		/* nexus node linkage */
+
+	uint32_t	h_flags;
+#define	HOST_ATTACH	(1U << 0)	/* host attach completed */
+#define	HOST_XOPEN	(1U << 2)	/* exclusive open */
+#define	HOST_SOPEN	(1U << 3)	/* shared open */
+};
+
+_NOTE(SCHEME_PROTECTS_DATA("stable data", sda_host::h_dip))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", sda_host::h_nslot))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", sda_host::h_dma))
+
+/*
+ * Useful function-like macros.
+ */
+#define	sda_setprop(s, p, v)	s->s_ops.so_setprop(s->s_prv, p, v)
+#define	sda_getprop(s, p, v)	s->s_ops.so_getprop(s->s_prv, p, v)
+
+/*
+ * sda_cmd.c
+ */
+void sda_cmd_init(void);
+void sda_cmd_fini(void);
+void sda_cmd_list_init(list_t *);
+void sda_cmd_list_fini(list_t *);
+sda_cmd_t *sda_cmd_alloc(sda_slot_t *, sda_index_t, uint32_t, sda_rtype_t,
+    void *, int);
+sda_cmd_t *sda_cmd_alloc_acmd(sda_slot_t *, sda_index_t, uint32_t, sda_rtype_t,
+    void *, int);
+void sda_cmd_free(sda_cmd_t *);
+sda_err_t sda_cmd_errno(sda_cmd_t *);
+void *sda_cmd_data(sda_cmd_t *);
+void sda_cmd_submit(sda_slot_t *, sda_cmd_t *, void (*)(sda_cmd_t *));
+void sda_cmd_resubmit_acmd(sda_slot_t *, sda_cmd_t *);
+void sda_cmd_notify(sda_cmd_t *, uint16_t, sda_err_t);
+sda_err_t sda_cmd_exec(sda_slot_t *, sda_cmd_t *, uint32_t *);
+
+/*
+ * sda_init.c
+ */
+sda_err_t sda_init_card(sda_slot_t *);
+
+/*
+ * sda_mem.c
+ */
+void sda_mem_init(struct modlinkage *);
+void sda_mem_fini(struct modlinkage *);
+uint32_t sda_mem_maxclk(sda_slot_t *);
+uint32_t sda_mem_getbits(uint32_t *, int, int);
+
+
+/*
+ * sda_nexus.c
+ */
+void sda_nexus_init(void);
+void sda_nexus_fini(void);
+void sda_nexus_register(sda_host_t *);
+void sda_nexus_unregister(sda_host_t *);
+int sda_nexus_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+int sda_nexus_open(dev_t *, int, int, cred_t *);
+int sda_nexus_close(dev_t, int, int, cred_t *);
+int sda_nexus_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+int sda_nexus_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
+    void *);
+void sda_nexus_remove(sda_slot_t *);
+void sda_nexus_insert(sda_slot_t *);
+void sda_nexus_reap(void *);
+
+/*
+ * sda_slot.c
+ */
+void sda_slot_init(sda_slot_t *);
+void sda_slot_fini(sda_slot_t *);
+void sda_slot_enter(sda_slot_t *);
+void sda_slot_exit(sda_slot_t *);
+boolean_t sda_slot_owned(sda_slot_t *);
+void sda_slot_attach(sda_slot_t *);
+void sda_slot_detach(sda_slot_t *);
+void sda_slot_reset(sda_slot_t *);
+void sda_slot_wakeup(sda_slot_t *);
+void sda_slot_detect(sda_slot_t *);
+int sda_slot_power_on(sda_slot_t *);
+void sda_slot_power_off(sda_slot_t *);
+void sda_slot_reset(sda_slot_t *);
+void sda_slot_shutdown(sda_slot_t *);
+void sda_slot_transfer(sda_slot_t *, sda_err_t);
+void sda_slot_mem_reset(sda_slot_t *, sda_err_t);
+void sda_slot_fault(sda_slot_t *, sda_fault_t);
+/*PRINTFLIKE2*/
+void sda_slot_err(sda_slot_t *, const char *, ...);
+/*PRINTFLIKE2*/
+void sda_slot_log(sda_slot_t *, const char *, ...);
+
+#ifdef	DEBUG
+#define	sda_slot_debug(...)	sda_slot_log(__VA_ARGS__)
+#else
+#define	sda_slot_debug(...)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _SYS_SDCARD_SDA_IMPL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/sdcard/sda_ioctl.h	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,87 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SDCARD_SDA_IOCTL_H
+#define	_SYS_SDCARD_SDA_IOCTL_H
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * These IOCTLs are private between the sdcard cfgadm plugin, and the sda
+ * framework.
+ */
+
+typedef enum {
+	SDA_CT_UNKNOWN,
+	SDA_CT_MMC,
+	SDA_CT_SDMEM,
+	SDA_CT_SDHC,
+	SDA_CT_SDCOMBO,
+	SDA_CT_SDIO	/* expand on this later */
+} sda_card_type_t;
+
+typedef struct {
+
+	sda_card_type_t	ci_type;
+
+	/* these are only valid for memory cards */
+	uint32_t	ci_mfg;
+	char		ci_oem[16];	/* mfg id */
+	char		ci_pid[16];	/* ASCIIZ product */
+	uint32_t	ci_serial;
+	uint8_t		ci_month;
+	uint8_t		ci_year;
+	uint8_t		ci_major;
+	uint8_t		ci_minor;
+} sda_card_info_t;
+
+struct sda_ap_control {
+	unsigned	cmd;
+	size_t		size;
+	void		*data;
+};
+
+#ifdef	_KERNEL
+struct sda_ap_control32 {
+	unsigned	cmd;
+	size32_t	size;
+	caddr32_t	data;
+};
+#endif
+
+/* AP_CONTROL commands */
+#define	SDA_CFGA_GET_CARD_INFO		1
+#define	SDA_CFGA_GET_DEVICE_PATH	2
+#define	SDA_CFGA_RESET_SLOT		3
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_SDCARD_SDA_IOCTL_H */
--- a/usr/src/uts/common/sys/sunddi.h	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/common/sys/sunddi.h	Fri Aug 08 11:50:24 2008 -0700
@@ -27,8 +27,6 @@
 #ifndef	_SYS_SUNDDI_H
 #define	_SYS_SUNDDI_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Sun Specific DDI definitions
  */
@@ -213,6 +211,9 @@
 #define	DDI_NT_SATA_ATTACHMENT_POINT	"ddi_ctl:attachment_point:sata"
 						/* sata attachment pt */
 
+#define	DDI_NT_SDCARD_ATTACHMENT_POINT	"ddi_ctl:attachment_point:sdcard"
+						/* sdcard attachment pt */
+
 #define	DDI_NT_PCI_ATTACHMENT_POINT	"ddi_ctl:attachment_point:pci"
 						/* PCI attachment pt */
 #define	DDI_NT_SBD_ATTACHMENT_POINT	"ddi_ctl:attachment_point:sbd"
--- a/usr/src/uts/intel/Makefile.intel.shared	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/intel/Makefile.intel.shared	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 #
 #	This makefile contains the common definitions for all intel
@@ -297,6 +295,8 @@
 DRV_KMODS	+= sctp
 DRV_KMODS	+= sctp6
 DRV_KMODS	+= sd
+DRV_KMODS	+= sdcard
+DRV_KMODS	+= sdhost
 DRV_KMODS	+= sgen
 DRV_KMODS	+= si3124
 DRV_KMODS	+= smbios
@@ -521,6 +521,7 @@
 MISC_KMODS	+= amsrc2
 MISC_KMODS	+= audiosup
 MISC_KMODS	+= bignum
+MISC_KMODS	+= blk2scsa
 MISC_KMODS	+= bootdev
 MISC_KMODS	+= busra
 MISC_KMODS	+= cmlb
@@ -563,6 +564,7 @@
 MISC_KMODS	+= rsmops
 MISC_KMODS	+= sata
 MISC_KMODS	+= scsi
+MISC_KMODS	+= sda
 MISC_KMODS	+= strategy
 MISC_KMODS	+= strplumb
 MISC_KMODS	+= sysinit
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/blk2scsa/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,106 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This makefile drives the production of the blk2scsa driver
+# intel architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= blk2scsa
+OBJECTS		= $(BLK2SCSA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(BLK2SCSA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_MISC_DIR)/$(MODULE)
+WARLOCK_OUT	= $(BLK2SCSA_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi"
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+WLCC	=	wlcc
+TOUCH	=	touch
+WARLOCK	=	warlock
+
+warlock:	$(WARLOCK_OK)
+
+$(WARLOCK_OK):	$(WARLOCK_OUT) 
+	$(WARLOCK) -c $(WLCMD_DIR)/blk2scsa.wlcmd \
+		$(WARLOCK_OUT) \
+		-l ../warlock/scsi.ll -l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+%.ll:	$(UTSBASE)/common/io/scsi/adapters/blk2scsa/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/sda/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,118 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# uts/intel/sda/Makefile
+#
+#	This makefile drives the production of the sda "misc"
+#	kernel module.
+#
+#	intel architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sda
+OBJECTS		= $(SDA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SDA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_MISC_DIR)/$(MODULE)
+WARLOCK_OUT	= $(SDA_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on blk2scsa module, scope limiting mapfile
+# Note that we have to allow CTFMERGE to use fuzzy matching
+#
+MAPFILE		= $(UTSBASE)/common/io/sdcard/impl/mapfile
+LDFLAGS		+= -dy -Nmisc/blk2scsa -B reduce -M $(MAPFILE)
+CTFMRGFLAGS	+= -f
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+WLCC		= wlcc
+TOUCH		= touch
+WARLOCK		= warlock
+
+warlock:	$(WARLOCK_OK)
+
+$(WARLOCK_OK):	$(WARLOCK_OUT)
+	$(TOUCH) $@
+
+%.ll:	$(UTSBASE)/common/io/sdcard/impl/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/sdcard/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,100 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# uts/intel/sdcard/Makefile
+#
+#	This makefile drives the production of the sdcard driver.
+#
+#	intel architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sdcard
+OBJECTS		= $(SDCARD_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SDCARD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on sda module
+#
+LDFLAGS += -dy -N misc/sda
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/sdhost/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,127 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# uts/intel/sdhost/Makefile
+#
+#	This makefile drives the production of the sdhost driver
+#	kernel module.
+#
+#	intel architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sdhost
+OBJECTS		= $(SDHOST_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SDHOST_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+WARLOCK_OUT	= $(SDHOST_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on scsi module
+#
+LDFLAGS += -dy -Nmisc/sda
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+WARLOCK		= warlock
+WLCC		= wlcc
+TOUCH		= touch
+SDA_LLS		= $(SDA_OBJS:%.o= -l ../sda/%.ll)
+BLK2SCSA_LLS	= $(BLK2SCSA_OBJS:%.o= -l ../blk2scsa/%.ll)
+
+warlock:	$(WARLOCK_OK)
+
+blk2scsa.wl:
+	@cd ../blk2scsa; pwd; $(MAKE) warlock
+
+sda.wl:
+	@cd ../sda; pwd; $(MAKE) warlock
+
+$(WARLOCK_OK):	$(WARLOCK_OUT) $(WLCMD_DIR)/sdhost.wlcmd blk2scsa.wl sda.wl
+	$(WARLOCK) -c $(WLCMD_DIR)/sdhost.wlcmd $(WARLOCK_OUT) \
+		$(SDA_LLS) $(BLK2SCSA_LLS) \
+		-l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+%.ll:	$(UTSBASE)/common/io/sdcard/adapters/sdhost/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
--- a/usr/src/uts/intel/warlock/Makefile	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/intel/warlock/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -20,11 +20,9 @@
 #
 
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # uts/intel/warlock/Makefile
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -54,7 +52,7 @@
 #	lock_lint rules
 #
 all:	warlock warlock.1394 warlock.audio warlock.ecpp warlock.scsi \
-	warlock.usb warlock.ib warlock.sata
+	warlock.usb warlock.ib warlock.sata warlock.sdcard
 
 warlock: $(MODULE).ok 
 
@@ -89,6 +87,7 @@
 	@cd ../sd; $(MAKE) clean; $(MAKE) warlock
 	@cd ../ses; $(MAKE) clean; $(MAKE) warlock
 	@cd ../st; $(MAKE) clean; $(MAKE) warlock
+	@cd ../blk2scsa; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/intel/glm; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/intel/mpt; $(MAKE) clean; $(MAKE) warlock
 
@@ -133,3 +132,7 @@
 	@cd ../ahci; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/intel/marvell88sx; \
 		$(MAKE) clean; $(MAKE) warlock
+
+warlock.sdcard:
+	@cd ../sda; $(MAKE) clean; $(MAKE) warlock
+	@cd ../sdhost; $(MAKE) clean; $(MAKE) warlock
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Fri Aug 08 11:50:24 2008 -0700
@@ -23,8 +23,6 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 #	This makefile contains the common definitions for all sparc
 #	implementation architecture independent modules.
 #
@@ -291,6 +289,9 @@
 DRV_KMODS	+= smp
 DRV_KMODS	+= dad
 DRV_KMODS	+= scsi_vhci
+DRV_KMODS	+= sdcard
+DRV_KMODS	+= sdhost
+DRV_KMODS	+= wbsd
 
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= audioens
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= audiovia823x
@@ -391,6 +392,8 @@
 MISC_KMODS	+= scsi_vhci_f_sym scsi_vhci_f_tpgs scsi_vhci_f_asym_sun
 MISC_KMODS	+= scsi_vhci_f_sym_hds
 MISC_KMODS	+= scsi_vhci_f_tape scsi_vhci_f_tpgs_tape
+MISC_KMODS	+= blk2scsa
+MISC_KMODS	+= sda
 
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= amsrc1
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= klmmod klmops
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/blk2scsa/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,105 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This makefile drives the production of the blk2scsa driver
+# sparc architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= blk2scsa
+OBJECTS		= $(BLK2SCSA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(BLK2SCSA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_MISC_DIR)/$(MODULE)
+WARLOCK_OUT	= $(BLK2SCSA_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi"
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+WLCC	=	wlcc
+TOUCH	=	touch
+WARLOCK	=	warlock
+
+warlock:	$(WARLOCK_OK)
+
+$(WARLOCK_OK):	$(WARLOCK_OUT)
+	$(WARLOCK) -c $(WLCMD_DIR)/blk2scsa.wlcmd \
+		$(WARLOCK_OUT) \
+		-l ../warlock/scsi.ll -l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+%.ll:	$(UTSBASE)/common/io/scsi/adapters/blk2scsa/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/sda/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,118 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# uts/sparc/sda/Makefile
+#
+#	This makefile drives the production of the sda "misc"
+#	kernel module.
+#
+#	sparc architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sda
+OBJECTS		= $(SDA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SDA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_MISC_DIR)/$(MODULE)
+WARLOCK_OUT	= $(SDA_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on blk2scsa module, scope limiting mapfile
+# Note that we have to allow CTFMERGE to use fuzzy matching
+#
+MAPFILE		= $(UTSBASE)/common/io/sdcard/impl/mapfile
+LDFLAGS		+= -dy -Nmisc/blk2scsa -B reduce -M $(MAPFILE)
+CTFMRGFLAGS	+= -f
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+WLCC		= wlcc
+TOUCH		= touch
+WARLOCK		= warlock
+
+warlock:	$(WARLOCK_OK)
+
+$(WARLOCK_OK):	$(WARLOCK_OUT)
+	$(TOUCH) $@
+
+%.ll:	$(UTSBASE)/common/io/sdcard/impl/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/sdcard/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,100 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# uts/sparc/sdcard/Makefile
+#
+#	This makefile drives the production of the sdcard driver.
+#
+#	sparc architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sdcard
+OBJECTS		= $(SDCARD_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SDCARD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on sda module
+#
+LDFLAGS += -dy -N misc/sda
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/sdhost/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,127 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# uts/sparc/sdhost/Makefile
+#
+#	This makefile drives the production of the sdhost driver
+#	kernel module.
+#
+#	sparc architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sdhost
+OBJECTS		= $(SDHOST_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SDHOST_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+WARLOCK_OUT	= $(SDHOST_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on scsi module
+#
+LDFLAGS += -dy -Nmisc/sda
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+WARLOCK		= warlock
+WLCC		= wlcc
+TOUCH		= touch
+SDA_LLS		= $(SDA_OBJS:%.o= -l ../sda/%.ll)
+BLK2SCSA_LLS	= $(BLK2SCSA_OBJS:%.o= -l ../blk2scsa/%.ll)
+
+warlock:	$(WARLOCK_OK)
+
+blk2scsa.wl:
+	@cd ../blk2scsa; pwd; $(MAKE) warlock
+
+sda.wl:
+	@cd ../sda; pwd; $(MAKE) warlock
+
+$(WARLOCK_OK):	$(WARLOCK_OUT) $(WLCMD_DIR)/sdhost.wlcmd blk2scsa.wl sda.wl
+	$(WARLOCK) -c $(WLCMD_DIR)/sdhost.wlcmd $(WARLOCK_OUT) \
+	$(SDA_LLS) $(BLK2SCSA_LLS) \
+	-l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+%.ll:	$(UTSBASE)/common/io/sdcard/adapters/sdhost/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
--- a/usr/src/uts/sparc/warlock/Makefile	Thu Aug 07 20:15:43 2008 -0600
+++ b/usr/src/uts/sparc/warlock/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -18,11 +18,9 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 #	sparc architecture dependent
 #
 # uts/sparc/warlock/Makefile
@@ -95,6 +93,7 @@
 	@cd ../ses; $(MAKE) clean; $(MAKE) warlock
 	@cd ../st; $(MAKE) clean; $(MAKE) warlock
 	@cd ../ssd; $(MAKE) clean; $(MAKE) warlock
+	@cd ../blk2scsa; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/sparc/uata; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/sparc/glm; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/sparc/mpt; $(MAKE) clean; $(MAKE) warlock
@@ -147,3 +146,8 @@
 	@cd ../nv_sata; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/sparc/marvell88sx; \
 		$(MAKE) clean; $(MAKE) warlock
+
+warlock.sdcard:
+	@cd ../sda; $(MAKE) clean; $(MAKE) warlock
+	@cd ../sdhost; $(MAKE) clean; $(MAKE) warlock
+	@cd ../wbsd; $(MAKE) clean; $(MAKE) warlock
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/wbsd/Makefile	Fri Aug 08 11:50:24 2008 -0700
@@ -0,0 +1,127 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# uts/sparc/wbsd/Makefile
+#
+#	This makefile drives the production of the wbsd driver
+#	kernel module.
+#
+#	sparc architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= wbsd
+OBJECTS		= $(WBSD_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(WBSD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+WARLOCK_OUT	= $(WBSD_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on scsi module
+#
+LDFLAGS += -dy -Nmisc/sda
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+WARLOCK		= warlock
+WLCC		= wlcc
+TOUCH		= touch
+SDA_LLS 	= $(SDA_OBJS:%.o= -l ../sda/%.ll)
+BLK2SCSA_LLS	= $(BLK2SCSA_OBJS:%.o= -l ../blk2scsa/%.ll)
+
+warlock:	$(WARLOCK_OK)
+
+blk2scsa.wl:
+	@cd ../blk2scsa; pwd; $(MAKE) warlock
+
+sda.wl:
+	@cd ../sda; pwd; $(MAKE) warlock
+
+$(WARLOCK_OK):	$(WARLOCK_OUT) $(WLCMD_DIR)/wbsd.wlcmd blk2scsa.wl sda.wl
+	$(WARLOCK) -c $(WLCMD_DIR)/wbsd.wlcmd $(WARLOCK_OUT) \
+		$(SDA_LLS) $(BLK2SCSA_LLS) \
+		-l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+%.ll:	$(UTSBASE)/common/io/sdcard/adapters/wbsd/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<