diff options
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 $@ $< |