summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/Makefile.lint3
-rw-r--r--usr/src/cmd/devfsadm/cfg_link.c50
-rw-r--r--usr/src/cmd/devfsadm/cfg_link.h5
-rw-r--r--usr/src/lib/cfgadm_plugins/Makefile6
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/Makefile70
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/Makefile.com70
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/amd64/Makefile33
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.c1933
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.h138
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/common/mapfile-vers36
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/i386/Makefile32
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/sdcard.xcl70
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/sparc/Makefile32
-rw-r--r--usr/src/lib/cfgadm_plugins/sdcard/sparcv9/Makefile33
-rw-r--r--usr/src/pkgdefs/Makefile6
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_i3864
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_sparc3
-rw-r--r--usr/src/pkgdefs/SUNWcsl/prototype_com4
-rw-r--r--usr/src/pkgdefs/SUNWcsl/prototype_i3864
-rw-r--r--usr/src/pkgdefs/SUNWcsl/prototype_sparc4
-rw-r--r--usr/src/pkgdefs/SUNWsdcard/Makefile35
-rw-r--r--usr/src/pkgdefs/SUNWsdcard/pkginfo.tmpl45
-rw-r--r--usr/src/pkgdefs/SUNWsdcard/postinstall141
-rw-r--r--usr/src/pkgdefs/SUNWsdcard/postremove44
-rw-r--r--usr/src/pkgdefs/SUNWsdcard/prototype_com48
-rw-r--r--usr/src/pkgdefs/SUNWsdcard/prototype_i38652
-rw-r--r--usr/src/pkgdefs/SUNWsdcard/prototype_sparc49
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/Makefile33
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/depend50
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/pkginfo.tmpl45
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/postinstall139
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/postremove36
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/prototype_com47
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/prototype_i38648
-rw-r--r--usr/src/pkgdefs/SUNWwbsd/prototype_sparc46
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i3869
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc9
-rw-r--r--usr/src/uts/common/Makefile.files17
-rw-r--r--usr/src/uts/common/Makefile.rules41
-rw-r--r--usr/src/uts/common/io/scsi/adapters/blk2scsa/blk2scsa.c1971
-rw-r--r--usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c1467
-rw-r--r--usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.h299
-rw-r--r--usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c1058
-rw-r--r--usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.h154
-rw-r--r--usr/src/uts/common/io/sdcard/impl/mapfile48
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_cmd.c355
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_host.c237
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_init.c654
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_mem.c653
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_mod.c82
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_nexus.c930
-rw-r--r--usr/src/uts/common/io/sdcard/impl/sda_slot.c896
-rw-r--r--usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c105
-rw-r--r--usr/src/uts/common/io/warlock/blk2scsa.wlcmd63
-rw-r--r--usr/src/uts/common/io/warlock/sdhost.wlcmd34
-rw-r--r--usr/src/uts/common/sys/Makefile18
-rw-r--r--usr/src/uts/common/sys/Makefile.syshdrs14
-rw-r--r--usr/src/uts/common/sys/scsi/adapters/blk2scsa.h182
-rw-r--r--usr/src/uts/common/sys/sdcard/sda.h327
-rw-r--r--usr/src/uts/common/sys/sdcard/sda_impl.h283
-rw-r--r--usr/src/uts/common/sys/sdcard/sda_ioctl.h87
-rw-r--r--usr/src/uts/common/sys/sunddi.h5
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared6
-rw-r--r--usr/src/uts/intel/blk2scsa/Makefile106
-rw-r--r--usr/src/uts/intel/sda/Makefile118
-rw-r--r--usr/src/uts/intel/sdcard/Makefile100
-rw-r--r--usr/src/uts/intel/sdhost/Makefile127
-rw-r--r--usr/src/uts/intel/warlock/Makefile11
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared7
-rw-r--r--usr/src/uts/sparc/blk2scsa/Makefile105
-rw-r--r--usr/src/uts/sparc/sda/Makefile118
-rw-r--r--usr/src/uts/sparc/sdcard/Makefile100
-rw-r--r--usr/src/uts/sparc/sdhost/Makefile127
-rw-r--r--usr/src/uts/sparc/warlock/Makefile10
-rw-r--r--usr/src/uts/sparc/wbsd/Makefile127
75 files changed, 14397 insertions, 57 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 43aa2c8735..b0af7db469 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -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 @@ COMMON_SUBDIRS = \
lib/auditd_plugins \
lib/brand/native \
lib/brand/sn1 \
+ lib/cfgadm_plugins/sdcard \
lib/crypt_modules \
lib/extendedFILE \
lib/libadt_jni \
diff --git a/usr/src/cmd/devfsadm/cfg_link.c b/usr/src/cmd/devfsadm/cfg_link.c
index 662ebea88a..8314b8a347 100644
--- a/usr/src/cmd/devfsadm/cfg_link.c
+++ b/usr/src/cmd/devfsadm/cfg_link.c
@@ -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 char *get_roothub(const char *path, void *cb_arg);
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 @@ static devfsadm_create_t cfg_create_cbt[] = {
},
{ "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 @@ static devfsadm_remove_t cfg_remove_cbt[] = {
},
{ "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 @@ sata_cfg_creat_cb(di_minor_t minor, di_node_t node)
}
(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 @@ sata_cfg_creat_cb(di_minor_t minor, di_node_t node)
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 @@ pci_cfg_iob_name(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
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);
}
diff --git a/usr/src/cmd/devfsadm/cfg_link.h b/usr/src/cmd/devfsadm/cfg_link.h
index ad7b5a949d..e2bb24864d 100644
--- a/usr/src/cmd/devfsadm/cfg_link.h
+++ b/usr/src/cmd/devfsadm/cfg_link.h
@@ -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 @@ extern "C" {
#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\\>).*)$"
diff --git a/usr/src/lib/cfgadm_plugins/Makefile b/usr/src/lib/cfgadm_plugins/Makefile
index 399a8e3a8d..7611450bcc 100644
--- a/usr/src/lib/cfgadm_plugins/Makefile
+++ b/usr/src/lib/cfgadm_plugins/Makefile
@@ -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
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/Makefile b/usr/src/lib/cfgadm_plugins/sdcard/Makefile
new file mode 100644
index 0000000000..938a045d0c
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/Makefile
@@ -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:
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/Makefile.com b/usr/src/lib/cfgadm_plugins/sdcard/Makefile.com
new file mode 100644
index 0000000000..40437c3ce9
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/Makefile.com
@@ -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)
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/amd64/Makefile b/usr/src/lib/cfgadm_plugins/sdcard/amd64/Makefile
new file mode 100644
index 0000000000..8b4f78dc58
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/amd64/Makefile
@@ -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)
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.c b/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.c
new file mode 100644
index 0000000000..b3b33dfa40
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.c
@@ -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");
+ }
+ }
+}
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.h b/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.h
new file mode 100644
index 0000000000..112055c87e
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/common/cfga_sdcard.h
@@ -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 */
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/common/mapfile-vers b/usr/src/lib/cfgadm_plugins/sdcard/common/mapfile-vers
new file mode 100644
index 0000000000..449c02eca3
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/common/mapfile-vers
@@ -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:
+ *;
+};
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/i386/Makefile b/usr/src/lib/cfgadm_plugins/sdcard/i386/Makefile
new file mode 100644
index 0000000000..e1900e1a66
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/i386/Makefile
@@ -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)
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/sdcard.xcl b/usr/src/lib/cfgadm_plugins/sdcard/sdcard.xcl
new file mode 100644
index 0000000000..1bf7857fd9
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/sdcard.xcl
@@ -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"
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/sparc/Makefile b/usr/src/lib/cfgadm_plugins/sdcard/sparc/Makefile
new file mode 100644
index 0000000000..e1900e1a66
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/sparc/Makefile
@@ -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)
diff --git a/usr/src/lib/cfgadm_plugins/sdcard/sparcv9/Makefile b/usr/src/lib/cfgadm_plugins/sdcard/sparcv9/Makefile
new file mode 100644
index 0000000000..8b4f78dc58
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/sdcard/sparcv9/Makefile
@@ -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)
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index 067ddb9ae0..bd5020a284 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -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 @@ sparc_SUBDIRS= \
SUNWstc.u \
SUNWus.u \
SUNWust1.v \
- SUNWust2.v
+ SUNWust2.v \
+ SUNWwbsd
sparc_XMODS=
@@ -370,6 +369,7 @@ COMMON_SUBDIRS= \
SUNWscplp \
SUNWscsa1394 \
SUNWscsip \
+ SUNWsdcard \
SUNWses \
SUNWsfe \
SUNWslpr \
diff --git a/usr/src/pkgdefs/SUNWckr/prototype_i386 b/usr/src/pkgdefs/SUNWckr/prototype_i386
index aa071a3f5a..cf004366d3 100644
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386
@@ -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/scsi_vhci/scsi_vhci_f_tpgs_tape 755 root sys
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 @@ d none kernel/misc/amd64 755 root sys
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
diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc
index 8b3a9fdde0..17b5db8dc4 100644
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc
@@ -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_tpgs 755 root sys
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
diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_com b/usr/src/pkgdefs/SUNWcsl/prototype_com
index 7faeab7b4f..6c8a7e5e0e 100644
--- a/usr/src/pkgdefs/SUNWcsl/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_com
@@ -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 @@ f none usr/lib/0@0.so.1 755 root bin
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
diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_i386 b/usr/src/pkgdefs/SUNWcsl/prototype_i386
index f81e603624..3cd861157d 100644
--- a/usr/src/pkgdefs/SUNWcsl/prototype_i386
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_i386
@@ -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 @@ s none usr/ccs/lib/amd64/liby.so=../../../lib/amd64/liby.so.1
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
diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_sparc b/usr/src/pkgdefs/SUNWcsl/prototype_sparc
index 3af9d3ae8a..9a2c498cf0 100644
--- a/usr/src/pkgdefs/SUNWcsl/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_sparc
@@ -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 @@ s none usr/ccs/lib/sparcv9/liby.so=../../../lib/sparcv9/liby.so.1
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
diff --git a/usr/src/pkgdefs/SUNWsdcard/Makefile b/usr/src/pkgdefs/SUNWsdcard/Makefile
new file mode 100644
index 0000000000..b15895afe0
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWsdcard/Makefile
@@ -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
diff --git a/usr/src/pkgdefs/SUNWsdcard/pkginfo.tmpl b/usr/src/pkgdefs/SUNWsdcard/pkginfo.tmpl
new file mode 100644
index 0000000000..775d8b19e1
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWsdcard/pkginfo.tmpl
@@ -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"
diff --git a/usr/src/pkgdefs/SUNWsdcard/postinstall b/usr/src/pkgdefs/SUNWsdcard/postinstall
new file mode 100644
index 0000000000..106e47eed7
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWsdcard/postinstall
@@ -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
diff --git a/usr/src/pkgdefs/SUNWsdcard/postremove b/usr/src/pkgdefs/SUNWsdcard/postremove
new file mode 100644
index 0000000000..3c58c77166
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWsdcard/postremove
@@ -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
diff --git a/usr/src/pkgdefs/SUNWsdcard/prototype_com b/usr/src/pkgdefs/SUNWsdcard/prototype_com
new file mode 100644
index 0000000000..0f6afd9379
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWsdcard/prototype_com
@@ -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
diff --git a/usr/src/pkgdefs/SUNWsdcard/prototype_i386 b/usr/src/pkgdefs/SUNWsdcard/prototype_i386
new file mode 100644
index 0000000000..f62e3a64e2
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWsdcard/prototype_i386
@@ -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
diff --git a/usr/src/pkgdefs/SUNWsdcard/prototype_sparc b/usr/src/pkgdefs/SUNWsdcard/prototype_sparc
new file mode 100644
index 0000000000..40a274e273
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWsdcard/prototype_sparc
@@ -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
diff --git a/usr/src/pkgdefs/SUNWwbsd/Makefile b/usr/src/pkgdefs/SUNWwbsd/Makefile
new file mode 100644
index 0000000000..b414b661c9
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/Makefile
@@ -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
diff --git a/usr/src/pkgdefs/SUNWwbsd/depend b/usr/src/pkgdefs/SUNWwbsd/depend
new file mode 100644
index 0000000000..857a9e45d3
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/depend
@@ -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
diff --git a/usr/src/pkgdefs/SUNWwbsd/pkginfo.tmpl b/usr/src/pkgdefs/SUNWwbsd/pkginfo.tmpl
new file mode 100644
index 0000000000..e67b841f24
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/pkginfo.tmpl
@@ -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"
diff --git a/usr/src/pkgdefs/SUNWwbsd/postinstall b/usr/src/pkgdefs/SUNWwbsd/postinstall
new file mode 100644
index 0000000000..51c1d4aa2a
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/postinstall
@@ -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
+
diff --git a/usr/src/pkgdefs/SUNWwbsd/postremove b/usr/src/pkgdefs/SUNWwbsd/postremove
new file mode 100644
index 0000000000..ee057ad860
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/postremove
@@ -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
diff --git a/usr/src/pkgdefs/SUNWwbsd/prototype_com b/usr/src/pkgdefs/SUNWwbsd/prototype_com
new file mode 100644
index 0000000000..03b81353c3
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/prototype_com
@@ -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
diff --git a/usr/src/pkgdefs/SUNWwbsd/prototype_i386 b/usr/src/pkgdefs/SUNWwbsd/prototype_i386
new file mode 100644
index 0000000000..46277b9c22
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/prototype_i386
@@ -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
diff --git a/usr/src/pkgdefs/SUNWwbsd/prototype_sparc b/usr/src/pkgdefs/SUNWwbsd/prototype_sparc
new file mode 100644
index 0000000000..6b5fd6c332
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwbsd/prototype_sparc
@@ -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
diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386
index 6c7769e8f8..0b961b97fd 100644
--- a/usr/src/pkgdefs/etc/exception_list_i386
+++ b/usr/src/pkgdefs/etc/exception_list_i386
@@ -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 @@ usr/lib/vscan/llib-lvscan.ln i386
# 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
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index 7365cb5698..5a9cef6611 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -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/include/libvscan.h sparc
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
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index e4d97623f1..65826a2483 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -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 @@ NXGE_HCALL_OBJS = \
#
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 @@ KICONV_TC_OBJS += kiconv_cck_common.o kiconv_tc.o
# 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
#
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 4e5501a52f..c0f22a9175 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -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 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/scsi/adapters/%.c
$(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 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/scsi/adapters/scsi_vhci/fops/%.c
$(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/sata/impl/%.c
$(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/impl/%.c
$(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))
diff --git a/usr/src/uts/common/io/scsi/adapters/blk2scsa/blk2scsa.c b/usr/src/uts/common/io/scsi/adapters/blk2scsa/blk2scsa.c
new file mode 100644
index 0000000000..19eb56b808
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/blk2scsa/blk2scsa.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c
new file mode 100644
index 0000000000..6587c22187
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.h b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.h
new file mode 100644
index 0000000000..a78c248ea5
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/adapters/sdhost/sdhost.h
@@ -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 */
diff --git a/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c
new file mode 100644
index 0000000000..9ad7ad2428
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.h b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.h
new file mode 100644
index 0000000000..b77ef1bd94
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/adapters/wbsd/wbsd.h
@@ -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 */
diff --git a/usr/src/uts/common/io/sdcard/impl/mapfile b/usr/src/uts/common/io/sdcard/impl/mapfile
new file mode 100644
index 0000000000..62bc1454e9
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/mapfile
@@ -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:
+ *;
+};
diff --git a/usr/src/uts/common/io/sdcard/impl/sda_cmd.c b/usr/src/uts/common/io/sdcard/impl/sda_cmd.c
new file mode 100644
index 0000000000..4524e3f03c
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/sda_cmd.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/impl/sda_host.c b/usr/src/uts/common/io/sdcard/impl/sda_host.c
new file mode 100644
index 0000000000..5ffb309fa0
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/sda_host.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/impl/sda_init.c b/usr/src/uts/common/io/sdcard/impl/sda_init.c
new file mode 100644
index 0000000000..e6cfce429d
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/sda_init.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/impl/sda_mem.c b/usr/src/uts/common/io/sdcard/impl/sda_mem.c
new file mode 100644
index 0000000000..336fe291ec
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/sda_mem.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/impl/sda_mod.c b/usr/src/uts/common/io/sdcard/impl/sda_mod.c
new file mode 100644
index 0000000000..34da209941
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/sda_mod.c
@@ -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));
+}
diff --git a/usr/src/uts/common/io/sdcard/impl/sda_nexus.c b/usr/src/uts/common/io/sdcard/impl/sda_nexus.c
new file mode 100644
index 0000000000..764280c4f2
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/sda_nexus.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/impl/sda_slot.c b/usr/src/uts/common/io/sdcard/impl/sda_slot.c
new file mode 100644
index 0000000000..8aacb2806b
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/impl/sda_slot.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c b/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c
new file mode 100644
index 0000000000..4b5d7356c0
--- /dev/null
+++ b/usr/src/uts/common/io/sdcard/targets/sdcard/sdcard.c
@@ -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));
+}
diff --git a/usr/src/uts/common/io/warlock/blk2scsa.wlcmd b/usr/src/uts/common/io/warlock/blk2scsa.wlcmd
new file mode 100644
index 0000000000..b72bc7c2ed
--- /dev/null
+++ b/usr/src/uts/common/io/warlock/blk2scsa.wlcmd
@@ -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
+
diff --git a/usr/src/uts/common/io/warlock/sdhost.wlcmd b/usr/src/uts/common/io/warlock/sdhost.wlcmd
new file mode 100644
index 0000000000..1414325c61
--- /dev/null
+++ b/usr/src/uts/common/io/warlock/sdhost.wlcmd
@@ -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
+
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 728860594a..0095caf0a2 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -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 @@ SCSITARGETSHDRS= \
sddef.h \
smp.h
+SCSIADHDRS= \
+ blk2scsa.h
+
SCSICADHDRS=
SCSIVHCIHDRS= \
@@ -866,6 +867,11 @@ SCSIVHCIHDRS= \
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 @@ CHECKHDRS= \
$(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 @@ CHECKHDRS= \
$(ROOTLVMHDRS) \
$(ROOTPCMCIAHDRS) \
$(ROOTSCSIHDRS) \
+ $(ROOTSCSIADHDRS) \
$(ROOTSCSICONFHDRS) \
$(ROOTSCSIGENHDRS) \
$(ROOTSCSIIMPLHDRS) \
$(ROOTSCSIVHCIHDRS) \
$(ROOTFCHDRS) \
+ $(ROOTSDCARDHDRS) \
$(ROOTSYSEVENTHDRS) \
$(ROOTCONTRACTHDRS) \
$(ROOTUSBHDRS) \
@@ -1144,11 +1154,13 @@ install_h: \
$(ROOTLVMHDRS) \
$(ROOTPCMCIAHDRS) \
$(ROOTSCSIHDRS) \
+ $(ROOTSCSIADHDRS) \
$(ROOTSCSICONFHDRS) \
$(ROOTSCSIGENHDRS) \
$(ROOTSCSIIMPLHDRS) \
$(ROOTSCSIVHCIHDRS) \
$(ROOTFCHDRS) \
+ $(ROOTSDCARDHDRS) \
$(ROOTSYSEVENTHDRS) \
$(ROOTCONTRACTHDRS) \
$(ROOTUSBHDRS) \
diff --git a/usr/src/uts/common/sys/Makefile.syshdrs b/usr/src/uts/common/sys/Makefile.syshdrs
index 26970f5219..66fdf546f2 100644
--- a/usr/src/uts/common/sys/Makefile.syshdrs
+++ b/usr/src/uts/common/sys/Makefile.syshdrs
@@ -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/targets/%.check: scsi/targets/%.h
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 @@ ROOTDIRS= \
$(ROOTDIR)/scsi/generic \
$(ROOTDIR)/scsi/impl \
$(ROOTDIR)/fc4 \
+ $(ROOTDIR)/sdcard \
$(ROOTDIR)/sysevent \
$(ROOTDIR)/contract \
$(ROOTDIR)/usb \
@@ -237,10 +239,13 @@ ROOTSCSIGENHDRS= $(SCSIGENHDRS:%=$(ROOTDIR)/scsi/generic/%)
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)/scsi/targets/%: scsi/targets/%
$(ROOTDIR)/fc4/%: fc4/%
$(INS.file)
+$(ROOTDIR)/sdcard/%: sdcard/%
+ $(INS.file)
+
$(ROOTDIR)/sysevent/%: sysevent/%
$(INS.file)
diff --git a/usr/src/uts/common/sys/scsi/adapters/blk2scsa.h b/usr/src/uts/common/sys/scsi/adapters/blk2scsa.h
new file mode 100644
index 0000000000..7b825dd6e4
--- /dev/null
+++ b/usr/src/uts/common/sys/scsi/adapters/blk2scsa.h
@@ -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 */
diff --git a/usr/src/uts/common/sys/sdcard/sda.h b/usr/src/uts/common/sys/sdcard/sda.h
new file mode 100644
index 0000000000..5f48cf8403
--- /dev/null
+++ b/usr/src/uts/common/sys/sdcard/sda.h
@@ -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 */
diff --git a/usr/src/uts/common/sys/sdcard/sda_impl.h b/usr/src/uts/common/sys/sdcard/sda_impl.h
new file mode 100644
index 0000000000..c71c897709
--- /dev/null
+++ b/usr/src/uts/common/sys/sdcard/sda_impl.h
@@ -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 */
diff --git a/usr/src/uts/common/sys/sdcard/sda_ioctl.h b/usr/src/uts/common/sys/sdcard/sda_ioctl.h
new file mode 100644
index 0000000000..616a571348
--- /dev/null
+++ b/usr/src/uts/common/sys/sdcard/sda_ioctl.h
@@ -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 */
diff --git a/usr/src/uts/common/sys/sunddi.h b/usr/src/uts/common/sys/sunddi.h
index af6acd290a..316d38682b 100644
--- a/usr/src/uts/common/sys/sunddi.h
+++ b/usr/src/uts/common/sys/sunddi.h
@@ -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 @@ extern "C" {
#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"
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 46ee989750..1fe6f1698d 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -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 += sad
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 += agpmaster
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 += rpcsec_gss
MISC_KMODS += rsmops
MISC_KMODS += sata
MISC_KMODS += scsi
+MISC_KMODS += sda
MISC_KMODS += strategy
MISC_KMODS += strplumb
MISC_KMODS += sysinit
diff --git a/usr/src/uts/intel/blk2scsa/Makefile b/usr/src/uts/intel/blk2scsa/Makefile
new file mode 100644
index 0000000000..b5430b3e0b
--- /dev/null
+++ b/usr/src/uts/intel/blk2scsa/Makefile
@@ -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 $@ $<
+
+
diff --git a/usr/src/uts/intel/sda/Makefile b/usr/src/uts/intel/sda/Makefile
new file mode 100644
index 0000000000..b93a79e71e
--- /dev/null
+++ b/usr/src/uts/intel/sda/Makefile
@@ -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 $@ $<
diff --git a/usr/src/uts/intel/sdcard/Makefile b/usr/src/uts/intel/sdcard/Makefile
new file mode 100644
index 0000000000..e13c487063
--- /dev/null
+++ b/usr/src/uts/intel/sdcard/Makefile
@@ -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
diff --git a/usr/src/uts/intel/sdhost/Makefile b/usr/src/uts/intel/sdhost/Makefile
new file mode 100644
index 0000000000..f033b2610c
--- /dev/null
+++ b/usr/src/uts/intel/sdhost/Makefile
@@ -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 $@ $<
diff --git a/usr/src/uts/intel/warlock/Makefile b/usr/src/uts/intel/warlock/Makefile
index 71699d6d75..af036d3c6e 100644
--- a/usr/src/uts/intel/warlock/Makefile
+++ b/usr/src/uts/intel/warlock/Makefile
@@ -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 @@ include $(UTSBASE)/intel/Makefile.intel
# 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 @@ warlock.scsi:
@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 @@ warlock.sata:
@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
diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared
index 1444e977d7..7ca35c4df0 100644
--- a/usr/src/uts/sparc/Makefile.sparc.shared
+++ b/usr/src/uts/sparc/Makefile.sparc.shared
@@ -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 += sgen
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 += pcicfg.e fcodem fcpci
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
diff --git a/usr/src/uts/sparc/blk2scsa/Makefile b/usr/src/uts/sparc/blk2scsa/Makefile
new file mode 100644
index 0000000000..47196be895
--- /dev/null
+++ b/usr/src/uts/sparc/blk2scsa/Makefile
@@ -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 $@ $<
+
diff --git a/usr/src/uts/sparc/sda/Makefile b/usr/src/uts/sparc/sda/Makefile
new file mode 100644
index 0000000000..ce70ed8bf4
--- /dev/null
+++ b/usr/src/uts/sparc/sda/Makefile
@@ -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 $@ $<
diff --git a/usr/src/uts/sparc/sdcard/Makefile b/usr/src/uts/sparc/sdcard/Makefile
new file mode 100644
index 0000000000..e972be756b
--- /dev/null
+++ b/usr/src/uts/sparc/sdcard/Makefile
@@ -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
diff --git a/usr/src/uts/sparc/sdhost/Makefile b/usr/src/uts/sparc/sdhost/Makefile
new file mode 100644
index 0000000000..e7fad4c1c8
--- /dev/null
+++ b/usr/src/uts/sparc/sdhost/Makefile
@@ -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 $@ $<
diff --git a/usr/src/uts/sparc/warlock/Makefile b/usr/src/uts/sparc/warlock/Makefile
index 5ab631aba1..a92c32fb56 100644
--- a/usr/src/uts/sparc/warlock/Makefile
+++ b/usr/src/uts/sparc/warlock/Makefile
@@ -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 @@ warlock.scsi:
@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 @@ warlock.sata:
@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
diff --git a/usr/src/uts/sparc/wbsd/Makefile b/usr/src/uts/sparc/wbsd/Makefile
new file mode 100644
index 0000000000..b1dc940827
--- /dev/null
+++ b/usr/src/uts/sparc/wbsd/Makefile
@@ -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 $@ $<