Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F152919402
D862.id1806.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
90 KB
Referenced Files
None
Subscribers
None
D862.id1806.diff
View Options
Index: etc/mtree/BSD.include.dist
===================================================================
--- etc/mtree/BSD.include.dist
+++ etc/mtree/BSD.include.dist
@@ -90,6 +90,8 @@
..
scsi
..
+ mmc
+ ..
..
clang
3.4.1
@@ -230,7 +232,7 @@
gssapi
..
infiniband
- complib
+ complib
..
iba
..
Index: include/Makefile
===================================================================
--- include/Makefile
+++ include/Makefile
@@ -39,7 +39,7 @@
LDIRS= bsm cam geom net net80211 netgraph netinet netinet6 \
netipsec netnatm netsmb nfs nfsclient nfsserver sys vm
-LSUBDIRS= cam/ata cam/scsi \
+LSUBDIRS= cam/ata cam/scsi cam/mmc \
dev/acpica dev/agp dev/an dev/bktr dev/ciss dev/filemon dev/firewire \
dev/hwpmc \
dev/ic dev/iicbus ${_dev_ieee488} dev/io dev/lmc dev/mfi dev/nvme \
Index: lib/libcam/Makefile
===================================================================
--- lib/libcam/Makefile
+++ lib/libcam/Makefile
@@ -37,7 +37,7 @@
cam_cdbparse.3 buff_encode_visit.3
.PATH: ${.CURDIR}/../../sys/cam/scsi ${.CURDIR}/../../sys/cam/ata \
- ${.CURDIR}/../../sys/cam
+ ${.CURDIR}/../../sys/cam ${.CURDIR}/../../sys/cam/mmc
SDIR= ${.CURDIR}/../../sys
CFLAGS+= -I${.CURDIR} -I${SDIR}
Index: sys/Makefile
===================================================================
--- sys/Makefile
+++ sys/Makefile
@@ -56,7 +56,7 @@
# You need the devel/global and one of editor/emacs* ports for that.
TAGS ${.CURDIR}/TAGS: ${.CURDIR}/cscope.files
rm -f ${.CURDIR}/TAGS
- cd ${.CURDIR}; xargs etags -a < ${.CURDIR}/cscope.files
+ cd ${.CURDIR}; xargs exctags -ea < ${.CURDIR}/cscope.files
# You need the textproc/glimpse ports for this.
glimpse:
Index: sys/amd64/conf/GENERIC
===================================================================
--- sys/amd64/conf/GENERIC
+++ sys/amd64/conf/GENERIC
@@ -356,4 +356,5 @@
device xenpci # Xen HVM Hypervisor services driver
# VMware support
+options CAM_DEBUG_COMPILE=65535
device vmx # VMware VMXNET3 Ethernet
Index: sys/arm/conf/BEAGLEBONE
===================================================================
--- sys/arm/conf/BEAGLEBONE
+++ sys/arm/conf/BEAGLEBONE
@@ -72,12 +72,12 @@
options NFSLOCKD
# Uncomment this for NFS root
-#options NFS_ROOT # NFS usable as /, requires NFSCL
-#options BOOTP_NFSROOT
-#options BOOTP_COMPAT
-#options BOOTP
-#options BOOTP_NFSV3
-#options BOOTP_WIRED_TO=cpsw0
+options NFS_ROOT # NFS usable as /, requires NFSCL
+options BOOTP_NFSROOT
+options BOOTP_COMPAT
+options BOOTP
+options BOOTP_NFSV3
+options BOOTP_WIRED_TO=cpsw0
# MMC/SD/SDIO Card slot support
device mmc # mmc/sd bus
@@ -139,3 +139,6 @@
options FDT
options FDT_DTB_STATIC
makeoptions FDT_DTS_FILE=beaglebone.dts
+
+options CAMDEBUG
+options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH|CAM_DEBUG_TRACE)
Index: sys/arm/ti/ti_sdhci.c
===================================================================
--- sys/arm/ti/ti_sdhci.c
+++ sys/arm/ti/ti_sdhci.c
@@ -39,6 +39,8 @@
#include <sys/rman.h>
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -52,6 +54,13 @@
#include <dev/mmc/mmcreg.h>
#include <dev/mmc/mmcbrvar.h>
+/* CAM includes */
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+
#include <dev/sdhci/sdhci.h>
#include "sdhci_if.h"
@@ -75,6 +84,11 @@
uint32_t sdhci_clkdiv;
boolean_t disable_highspeed;
boolean_t force_card_present;
+
+ struct mtx sc_mtx;
+ struct cam_devq *devq;
+ struct cam_sim *sim;
+ struct cam_path *path;
};
/*
@@ -120,6 +134,11 @@
#define MMCHS_SD_CAPA_VS30 (1 << 25)
#define MMCHS_SD_CAPA_VS33 (1 << 24)
+/* Forward declarations, CAM-relataed */
+static void ti_sdhci_cam_poll(struct cam_sim *);
+static void ti_sdhci_cam_action(struct cam_sim *, union ccb *);
+static int ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *);
+
static inline uint32_t
ti_mmchs_read_4(struct ti_sdhci_softc *sc, bus_size_t off)
{
@@ -607,7 +626,39 @@
bus_generic_probe(dev);
bus_generic_attach(dev);
- sdhci_start_slot(&sc->slot);
+// sdhci_start_slot(&sc->slot);
+
+ /* CAM-specific init */
+ mtx_init(&sc->sc_mtx, "tisdhcimtx", NULL, MTX_DEF);
+
+ if ((sc->devq = cam_simq_alloc(1)) == NULL) {
+ err = ENOMEM;
+ goto fail;
+ }
+
+ sc->sim = cam_sim_alloc(ti_sdhci_cam_action, ti_sdhci_cam_poll,
+ "ti_sdhci", sc, device_get_unit(dev),
+ &sc->sc_mtx, 1, 1, sc->devq);
+
+ if (sc->sim == NULL) {
+ cam_simq_free(sc->devq);
+ device_printf(dev, "cannot allocate CAM SIM\n");
+ err = EINVAL;
+ goto fail;
+ }
+
+ mtx_lock(&sc->sc_mtx);
+ if (xpt_bus_register(sc->sim, dev, 0) != 0) {
+ device_printf(dev,
+ "cannot register SCSI pass-through bus\n");
+ cam_sim_free(sc->sim, FALSE);
+ cam_simq_free(sc->devq);
+ mtx_unlock(&sc->sc_mtx);
+ err = EINVAL;
+ goto fail;
+ }
+ mtx_unlock(&sc->sc_mtx);
+ /* End CAM-specific init */
return (0);
@@ -619,6 +670,16 @@
if (sc->mem_res)
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+ if (sc->sim != NULL) {
+ mtx_lock(&sc->sc_mtx);
+ xpt_bus_deregister(cam_sim_path(sc->sim));
+ cam_sim_free(sc->sim, FALSE);
+ mtx_unlock(&sc->sc_mtx);
+ }
+
+ if (sc->devq != NULL)
+ cam_simq_free(sc->devq);
+
return (err);
}
@@ -637,6 +698,160 @@
return (ENXIO);
}
+static void
+ti_sdhci_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
+{
+ struct ti_sdhci_softc *sc;
+
+ sc = cam_sim_softc(sim);
+
+ sdhci_request_cam(&sc->slot, ccb);
+}
+
+static void
+ti_sdhci_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct ti_sdhci_softc *sc;
+
+ sc = cam_sim_softc(sim);
+ if (sc == NULL) {
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ xpt_done(ccb);
+ return;
+ }
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ device_printf(sc->dev, "action: func_code %0x\n", ccb->ccb_h.func_code);
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi;
+
+ cpi = &ccb->cpi;
+ cpi->version_num = 1;
+ cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_16;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = 0;
+ cpi->max_lun = 0;
+ cpi->initiator_id = 1;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Deglitch Networks", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 100; /* XXX WTF? */
+ cpi->protocol = PROTO_MMCSD;
+ cpi->protocol_version = SCSI_REV_0;
+ cpi->transport = XPORT_MMCSD;
+ cpi->transport_version = 0;
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ device_printf(sc->dev, "Got XPT_GET_TRAN_SETTINGS\n");
+
+ cts->protocol = PROTO_MMCSD;
+ cts->protocol_version = 0;
+ cts->transport = XPORT_MMCSD;
+ cts->transport_version = 0;
+ cts->xport_specific.valid = 0;
+ cts->proto_specific.mmc.host_ocr = sc->slot.host.host_ocr;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ device_printf(sc->dev, "Got XPT_SET_TRAN_SETTINGS, should update IOS...\n");
+ ti_sdhci_cam_settran_settings(sc, ccb);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_RESET_BUS:
+ device_printf(sc->dev, "Got XPT_RESET_BUS, ACK it...\n");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_MMC_IO:
+ /*
+ * Here is the HW-dependent part of
+ * sending the command to the underlying h/w
+ * At some point in the future an interrupt comes.
+ * Then the request will be marked as completed.
+ */
+ device_printf(sc->dev, "Got XPT_MMC_IO\n");
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+
+ ti_sdhci_handle_mmcio(sim, ccb);
+ return;
+ /* NOTREACHED */
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+ return;
+}
+
+static void
+ti_sdhci_cam_poll(struct cam_sim *sim)
+{
+ return;
+}
+
+static int
+ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *ccb)
+{
+ struct sdhci_slot *slot;
+ struct mmc_ios *ios;
+ struct mmc_ios *new_ios;
+ uint32_t val32;
+ struct ccb_trans_settings_mmc *cts;
+
+ slot = &sc->slot;
+ ios = &slot->host.ios;
+
+ cts = &ccb->cts.proto_specific.mmc;
+ new_ios = &cts->ios;
+
+ /* Update only requested fields */
+ if (cts->ios_valid & MMC_CLK)
+ ios->clock = new_ios->clock;
+ if (cts->ios_valid & MMC_VDD) {
+ ios->vdd = new_ios->vdd;
+ device_printf(sc->dev, "VDD => %d\n", ios->vdd);
+ }
+ if (cts->ios_valid & MMC_CS)
+ ios->chip_select = new_ios->chip_select;
+ if (cts->ios_valid & MMC_BW)
+ ios->bus_width = new_ios->bus_width;
+ if (cts->ios_valid & MMC_PM)
+ ios->power_mode = new_ios->power_mode;
+ if (cts->ios_valid & MMC_BT)
+ ios->timing = new_ios->timing;
+ if (cts->ios_valid & MMC_BM)
+ ios->bus_mode = new_ios->bus_mode;
+ /*
+ * There is an 8-bit-bus bit in the MMCHS control register which, when
+ * set, overrides the 1 vs 4 bit setting in the standard SDHCI
+ * registers. Set that bit first according to whether an 8-bit bus is
+ * requested, then let the standard driver handle everything else.
+ */
+ val32 = ti_mmchs_read_4(sc, MMCHS_CON);
+ if (ios->bus_width == bus_width_8)
+ ti_mmchs_write_4(sc, MMCHS_CON, val32 | MMCHS_CON_DW8);
+ else
+ ti_mmchs_write_4(sc, MMCHS_CON, val32 & ~MMCHS_CON_DW8);
+
+ return (sdhci_update_ios_cam(slot));
+}
+
static device_method_t ti_sdhci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_sdhci_probe),
Index: sys/cam/cam_ccb.h
===================================================================
--- sys/cam/cam_ccb.h
+++ sys/cam/cam_ccb.h
@@ -41,6 +41,7 @@
#include <cam/cam_debug.h>
#include <cam/scsi/scsi_all.h>
#include <cam/ata/ata_all.h>
+#include <cam/mmc/mmc_all.h>
/* General allocation length definitions for CCB structures */
#define IOCDBLEN CAM_MAX_CDBLEN /* Space for CDB bytes/pointer */
@@ -223,6 +224,8 @@
/* Notify Host Target driver of event */
XPT_NOTIFY_ACKNOWLEDGE = 0x37 | XPT_FC_QUEUED | XPT_FC_USER_CCB,
/* Acknowledgement of event */
+ XPT_MMC_IO = 0x38 | XPT_FC_DEV_QUEUED,
+ /* Execute the requested ATA I/O operation */
/* Vendor Unique codes: 0x80->0x8F */
XPT_VUNIQUE = 0x80
@@ -249,6 +252,7 @@
PROTO_ATAPI, /* AT Attachment Packetized Interface */
PROTO_SATAPM, /* SATA Port Multiplier */
PROTO_SEMB, /* SATA Enclosure Management Bridge */
+ PROTO_MMCSD, /* MMC, SD, SDIO */
} cam_proto;
typedef enum {
@@ -264,6 +268,7 @@
XPORT_SATA, /* Serial AT Attachment */
XPORT_ISCSI, /* iSCSI */
XPORT_SRP, /* SCSI RDMA Protocol */
+ XPORT_MMCSD, /* MMC, SD, SDIO card */
} cam_xport;
#define XPORT_IS_ATA(t) ((t) == XPORT_ATA || (t) == XPORT_SATA)
@@ -739,6 +744,15 @@
u_int init_id; /* initiator id of who selected */
};
+/*
+ * MMC I/O Request CCB used for the XPT_MMC_IO function code.
+ */
+struct ccb_mmcio {
+ struct ccb_hdr ccb_h;
+ union ccb *next_ccb; /* Ptr for next CCB for action */
+ struct mmc_command cmd;
+};
+
struct ccb_accept_tio {
struct ccb_hdr ccb_h;
cdb_t cdb_io; /* Union for CDB bytes/pointer */
@@ -940,6 +954,26 @@
#define CTS_SATA_CAPS_D_APST 0x00020000
};
+/*
+ * Basically, what earlier was done with updating IOS
+ * now can be done using SET_TRAN_SETTINGS
+ */
+#include <cam/mmc/mmc_bus.h>
+struct ccb_trans_settings_mmc {
+ struct mmc_ios ios;
+#define MMC_CLK (1 << 1)
+#define MMC_VDD (1 << 2)
+#define MMC_CS (1 << 3)
+#define MMC_BW (1 << 4)
+#define MMC_PM (1 << 5)
+#define MMC_BT (1 << 6)
+#define MMC_BM (1 << 7)
+ uint32_t ios_valid;
+
+/* The folowing is used only for GET_TRAN_SETTINGS */
+ uint32_t host_ocr;
+};
+
/* Get/Set transfer rate/width/disconnection/tag queueing settings */
struct ccb_trans_settings {
struct ccb_hdr ccb_h;
@@ -952,6 +986,7 @@
u_int valid; /* Which fields to honor */
struct ccb_trans_settings_ata ata;
struct ccb_trans_settings_scsi scsi;
+ struct ccb_trans_settings_mmc mmc;
} proto_specific;
union {
u_int valid; /* Which fields to honor */
@@ -1203,6 +1238,7 @@
struct ccb_ataio ataio;
struct ccb_dev_advinfo cdai;
struct ccb_async casync;
+ struct ccb_mmcio mmcio;
};
__BEGIN_DECLS
Index: sys/cam/cam_xpt.c
===================================================================
--- sys/cam/cam_xpt.c
+++ sys/cam/cam_xpt.c
@@ -385,7 +385,7 @@
}
return (error);
}
-
+
static int
xptdoioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
{
@@ -1022,6 +1022,8 @@
else if (path->device->protocol == PROTO_SEMB)
semb_print_ident(
(struct sep_identify_data *)&path->device->ident_data);
+ else if (path->device->protocol == PROTO_MMCSD)
+ mmc_print_ident(&path->device->mmc_ident_data);
else
printf("Unknown protocol device\n");
if (path->device->serial_num_len > 0) {
@@ -1075,6 +1077,8 @@
else if (path->device->protocol == PROTO_SEMB)
semb_print_ident_short(
(struct sep_identify_data *)&path->device->ident_data);
+ else if (path->device->protocol == PROTO_MMCSD)
+ mmc_print_ident(&path->device->mmc_ident_data);
else
printf("Unknown protocol device");
if (path->device->serial_num_len > 0)
@@ -1315,7 +1319,7 @@
cur_pattern = &patterns[i].pattern.device_pattern;
- /* Error out if mutually exclusive options are specified. */
+ /* Error out if mutually exclusive options are specified. */
if ((cur_pattern->flags & (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID))
== (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID))
return(DM_RET_ERROR);
@@ -2494,6 +2498,7 @@
if (start_ccb->ccb_h.func_code == XPT_ATA_IO)
start_ccb->ataio.resid = 0;
/* FALLTHROUGH */
+ case XPT_MMC_IO:
case XPT_RESET_DEV:
case XPT_ENG_EXEC:
case XPT_SMP_IO:
@@ -3898,6 +3903,9 @@
case XPORT_SATA:
new_bus->xport = ata_get_xport();
break;
+ case XPORT_MMCSD:
+ new_bus->xport = mmc_get_xport();
+ break;
default:
new_bus->xport = &xport_default;
break;
Index: sys/cam/cam_xpt_internal.h
===================================================================
--- sys/cam/cam_xpt_internal.h
+++ sys/cam/cam_xpt_internal.h
@@ -88,6 +88,7 @@
uint32_t rcap_len;
uint8_t *rcap_buf;
struct ata_params ident_data;
+ struct mmc_params mmc_ident_data;
u_int8_t inq_flags; /*
* Current settings for inquiry flags.
* This allows us to override settings
@@ -165,6 +166,7 @@
struct xpt_xport * scsi_get_xport(void);
struct xpt_xport * ata_get_xport(void);
+struct xpt_xport * mmc_get_xport(void);
struct cam_ed * xpt_alloc_device(struct cam_eb *bus,
struct cam_et *target,
Index: sys/cam/mmc/mmc.h
===================================================================
--- /dev/null
+++ sys/cam/mmc/mmc.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2014 Ilya Bakulin. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef CAM_MMC_H
+#define CAM_MMC_H
+
+/*
+ * This structure describes an MMC/SD card
+ */
+struct mmc_params {
+ u_int8_t model[40]; /* Card model */
+} __packed;
+
+#endif
Index: sys/cam/mmc/mmc_all.h
===================================================================
--- /dev/null
+++ sys/cam/mmc/mmc_all.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2014 Ilya Bakulin. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * MMC function that should be visible to the CAM subsystem
+ * and are somehow useful should be declared here
+ *
+ * Like in other *_all.h, it's also a nice place to include
+ * some other transport-specific headers.
+ */
+
+#ifndef CAM_MMC_ALL_H
+#define CAM_MMC_ALL_H
+
+#include <cam/mmc/mmc.h>
+#include <cam/mmc/mmcreg.h>
+
+void mmc_print_ident(struct mmc_params *ident_data);
+
+#endif
Index: sys/cam/mmc/mmc_bus.h
===================================================================
--- /dev/null
+++ sys/cam/mmc/mmc_bus.h
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef DEV_MMC_BRIDGE_H
+#define DEV_MMC_BRIDGE_H
+
+/*
+ * This file defines interfaces for the mmc bridge. The names chosen
+ * are similar to or the same as the names used in Linux to allow for
+ * easy porting of what Linux calls mmc host drivers. I use the
+ * FreeBSD terminology of bridge and bus for consistancy with other
+ * drivers in the system. This file corresponds roughly to the Linux
+ * linux/mmc/host.h file.
+ *
+ * A mmc bridge is a chipset that can have one or more mmc and/or sd
+ * cards attached to it. mmc cards are attached on a bus topology,
+ * while sd and sdio cards are attached using a star topology (meaning
+ * in practice each sd card has its own, independent slot). Each
+ * mmcbr is assumed to be derived from the mmcbr. This is done to
+ * allow for easier addition of bridges (as each bridge does not need
+ * to be added to the mmcbus file).
+ *
+ * Attached to the mmc bridge is an mmcbus. The mmcbus is described
+ * in dev/mmc/bus.h.
+ */
+
+
+/*
+ * mmc_ios is a structure that is used to store the state of the mmc/sd
+ * bus configuration. This include the bus' clock speed, its voltage,
+ * the bus mode for command output, the SPI chip select, some power
+ * states and the bus width.
+ */
+enum mmc_vdd {
+ vdd_150 = 0, vdd_155, vdd_160, vdd_165, vdd_170, vdd_180,
+ vdd_190, vdd_200, vdd_210, vdd_220, vdd_230, vdd_240, vdd_250,
+ vdd_260, vdd_270, vdd_280, vdd_290, vdd_300, vdd_310, vdd_320,
+ vdd_330, vdd_340, vdd_350, vdd_360
+};
+
+enum mmc_power_mode {
+ power_off = 0, power_up, power_on
+};
+
+enum mmc_bus_mode {
+ opendrain = 1, pushpull
+};
+
+enum mmc_chip_select {
+ cs_dontcare = 0, cs_high, cs_low
+};
+
+enum mmc_bus_width {
+ bus_width_1 = 0, bus_width_4 = 2, bus_width_8 = 3
+};
+
+enum mmc_bus_timing {
+ bus_timing_normal = 0, bus_timing_hs
+};
+
+struct mmc_ios {
+ uint32_t clock; /* Speed of the clock in Hz to move data */
+ enum mmc_vdd vdd; /* Voltage to apply to the power pins/ */
+ enum mmc_bus_mode bus_mode;
+ enum mmc_chip_select chip_select;
+ enum mmc_bus_width bus_width;
+ enum mmc_power_mode power_mode;
+ enum mmc_bus_timing timing;
+};
+
+enum mmc_card_mode {
+ mode_mmc, mode_sd
+};
+
+struct mmc_host {
+ int f_min;
+ int f_max;
+ uint32_t host_ocr;
+ uint32_t ocr;
+ uint32_t caps;
+#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */
+#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */
+#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */
+ enum mmc_card_mode mode;
+ struct mmc_ios ios; /* Current state of the host */
+};
+
+#endif /* DEV_MMC_BRIDGE_H */
Index: sys/cam/mmc/mmc_xpt.c
===================================================================
--- /dev/null
+++ sys/cam/mmc/mmc_xpt.c
@@ -0,0 +1,1116 @@
+/*-
+ * Copyright (c) 2013,2014 Ilya Bakulin <ilya@bakulin.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/time.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/interrupt.h>
+#include <sys/sbuf.h>
+
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/condvar.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_queue.h>
+#include <cam/cam_periph.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_xpt_internal.h>
+#include <cam/cam_debug.h>
+
+#include <cam/mmc/mmc.h>
+#include <cam/mmc/mmc_bus.h>
+
+#include <machine/stdarg.h> /* for xpt_print below */
+#include <machine/_inttypes.h> /* for PRIu64 */
+#include "opt_cam.h"
+
+struct xpt_xport *mmc_get_xport(void);
+static struct cam_ed * mmc_alloc_device(struct cam_eb *bus,
+ struct cam_et *target,
+ lun_id_t lun_id);
+static void mmc_dev_async(u_int32_t async_code,
+ struct cam_eb *bus,
+ struct cam_et *target,
+ struct cam_ed *device,
+ void *async_arg);
+static void mmc_action(union ccb *start_ccb);
+static void mmc_dev_advinfo(union ccb *start_ccb);
+static void mmc_announce_periph(struct cam_periph *periph);
+static void mmc_scan_bus(struct cam_periph *periph, union ccb *ccb);
+static void mmc_scan_lun(struct cam_periph *periph,
+ struct cam_path *path, cam_flags flags,
+ union ccb *ccb);
+static void xptscandone(struct cam_periph *periph, union ccb *done_ccb);
+
+/* Supplementary methods */
+static void mmc_build_app_op_cond(union ccb *ccb, uint32_t ocr);
+
+/* mmcprobe methods */
+static cam_status mmcprobe_register(struct cam_periph *periph,
+ void *arg);
+static void mmcprobe_start(struct cam_periph *periph, union ccb *start_ccb);
+static void mmcprobe_cleanup(struct cam_periph *periph);
+static void mmcprobe_done(struct cam_periph *periph, union ccb *done_ccb);
+
+static const char * get_action_desc(int action);
+
+static struct xpt_xport mmc_xport = {
+ .alloc_device = mmc_alloc_device,
+ .action = mmc_action,
+ .async = mmc_dev_async,
+ .announce = mmc_announce_periph,
+};
+
+struct xpt_xport *
+mmc_get_xport(void)
+{
+ return (&mmc_xport);
+}
+
+static void
+xptscandone(struct cam_periph *periph, union ccb *done_ccb)
+{
+
+ xpt_free_path(done_ccb->ccb_h.path);
+ xpt_free_ccb(done_ccb);
+}
+
+/* XPort functions -- an interface to CAM at periph side */
+
+static struct cam_ed *
+mmc_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
+{
+ struct cam_ed *device;
+
+ printf("mmc_alloc_device()\n");
+
+ device = xpt_alloc_device(bus, target, lun_id);
+ if (device == NULL)
+ return (NULL);
+
+ device->quirk = NULL;
+ device->mintags = 0;
+ device->maxtags = 0;
+ bzero(&device->inq_data, sizeof(device->inq_data));
+ device->inq_flags = 0;
+ device->queue_flags = 0;
+ device->serial_num = NULL;
+ device->serial_num_len = 0;
+ return (device);
+}
+
+static void
+mmc_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target,
+ struct cam_ed *device, void *async_arg)
+{
+ cam_status status;
+ struct cam_path newpath;
+
+ printf("mmc_dev_async(async_code=0x%x, path_id=%d, target_id=%x, lun_id=%" PRIu64 "\n",
+ async_code,
+ bus->path_id,
+ target->target_id,
+ device->lun_id);
+ /*
+ * We only need to handle events for real devices.
+ */
+ if (target->target_id == CAM_TARGET_WILDCARD
+ || device->lun_id == CAM_LUN_WILDCARD)
+ return;
+
+ /*
+ * We need our own path with wildcards expanded to
+ * handle certain types of events.
+ */
+ if ((async_code == AC_SENT_BDR)
+ || (async_code == AC_BUS_RESET)
+ || (async_code == AC_INQ_CHANGED))
+ status = xpt_compile_path(&newpath, NULL,
+ bus->path_id,
+ target->target_id,
+ device->lun_id);
+ else
+ status = CAM_REQ_CMP_ERR;
+
+ printf("mmc_async: status = 0x%x\n", status);
+
+ if (status == CAM_REQ_CMP) {
+ if (async_code == AC_INQ_CHANGED) {
+ /*
+ * We've sent a start unit command, or
+ * something similar to a device that
+ * may have caused its inquiry data to
+ * change. So we re-scan the device to
+ * refresh the inquiry data for it.
+ */
+ mmc_scan_lun(newpath.periph, &newpath,
+ CAM_EXPECT_INQ_CHANGE, NULL);
+ } else {
+ /* We need to reinitialize device after reset. */
+ mmc_scan_lun(newpath.periph, &newpath,
+ 0, NULL);
+ }
+ xpt_release_path(&newpath);
+ } else if (async_code == AC_LOST_DEVICE &&
+ (device->flags & CAM_DEV_UNCONFIGURED) == 0) {
+ device->flags |= CAM_DEV_UNCONFIGURED;
+ xpt_release_device(device);
+ } else if (async_code == AC_TRANSFER_NEG) {
+ struct ccb_trans_settings *settings;
+ struct cam_path path;
+
+ printf("AC_TRANSFER_NEG\n");
+ settings = (struct ccb_trans_settings *)async_arg;
+ xpt_compile_path(&path, NULL, bus->path_id, target->target_id,
+ device->lun_id);
+/* ata_set_transfer_settings(settings, &path, TRUE); */
+ xpt_release_path(&path);
+ }
+}
+
+typedef struct {
+ union ccb *request_ccb;
+ struct ccb_pathinq *cpi;
+ int counter;
+} mmc_scan_bus_info;
+
+static void
+mmc_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
+{
+ struct cam_path *path;
+ union ccb *work_ccb, *reset_ccb;
+ mmc_scan_bus_info *scan_info;
+ struct mtx *mtx;
+ cam_status status;
+
+ CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("mmc_scan_bus! func_code=%x\n", request_ccb->ccb_h.func_code));
+
+ switch (request_ccb->ccb_h.func_code) {
+ case XPT_SCAN_BUS:
+ CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("XPT_SCAN_BUS\n"));
+ case XPT_SCAN_TGT:
+ CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("XPT_SCAN_TGT\n"));
+ /* Find out the characteristics of the bus */
+ work_ccb = xpt_alloc_ccb_nowait();
+ if (work_ccb == NULL) {
+ request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_done(request_ccb);
+ return;
+ }
+ xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path,
+ request_ccb->ccb_h.pinfo.priority);
+ work_ccb->ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action(work_ccb);
+ if (work_ccb->ccb_h.status != CAM_REQ_CMP) {
+ request_ccb->ccb_h.status = work_ccb->ccb_h.status;
+ xpt_free_ccb(work_ccb);
+ xpt_done(request_ccb);
+ return;
+ }
+
+ /* We may need to reset bus first, if we haven't done it yet. */
+ if (!(work_ccb->cpi.hba_misc & PIM_NOBUSRESET) &&
+ !timevalisset(&request_ccb->ccb_h.path->bus->last_reset)) {
+ reset_ccb = xpt_alloc_ccb_nowait();
+ if (reset_ccb == NULL) {
+ request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_free_ccb(work_ccb);
+ xpt_done(request_ccb);
+ return;
+ }
+ xpt_setup_ccb(&reset_ccb->ccb_h, request_ccb->ccb_h.path,
+ CAM_PRIORITY_NONE);
+ reset_ccb->ccb_h.func_code = XPT_RESET_BUS;
+ xpt_action(reset_ccb);
+ if (reset_ccb->ccb_h.status != CAM_REQ_CMP) {
+ request_ccb->ccb_h.status = reset_ccb->ccb_h.status;
+ xpt_free_ccb(reset_ccb);
+ xpt_free_ccb(work_ccb);
+ xpt_done(request_ccb);
+ return;
+ }
+ xpt_free_ccb(reset_ccb);
+ }
+
+ /* Save some state for use while we probe for devices */
+ scan_info = (mmc_scan_bus_info *)
+ malloc(sizeof(mmc_scan_bus_info), M_CAMXPT, M_NOWAIT);
+ if (scan_info == NULL) {
+ request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_free_ccb(work_ccb);
+ xpt_done(request_ccb);
+ return;
+ }
+ scan_info->request_ccb = request_ccb;
+ scan_info->cpi = &work_ccb->cpi;
+ scan_info->counter = 0;
+
+ work_ccb = xpt_alloc_ccb_nowait();
+ if (work_ccb == NULL) {
+ free(scan_info, M_CAMXPT);
+ request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_done(request_ccb);
+ break;
+ }
+ mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path);
+ goto scan_next;
+
+ case XPT_SCAN_LUN:
+ CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("XPT_SCAN_LUN\n"));
+ work_ccb = request_ccb;
+ /* Reuse the same CCB to query if a device was really found */
+ scan_info = (mmc_scan_bus_info *)work_ccb->ccb_h.ppriv_ptr0;
+ mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path);
+ mtx_lock(mtx);
+ /* Free the current request path- we're done with it. */
+ xpt_free_path(work_ccb->ccb_h.path);
+ mtx_unlock(mtx);
+ xpt_free_ccb(work_ccb);
+ xpt_free_ccb((union ccb *)scan_info->cpi);
+ request_ccb = scan_info->request_ccb;
+ free(scan_info, M_CAMXPT);
+ request_ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(request_ccb);
+ CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("SCAN_LUN -- done with it\n"));
+ return;
+
+scan_next:
+ status = xpt_create_path(&path, NULL,
+ scan_info->request_ccb->ccb_h.path_id, 0, 0);
+ if (status != CAM_REQ_CMP) {
+ if (request_ccb->ccb_h.func_code == XPT_SCAN_LUN)
+ mtx_unlock(mtx);
+ printf("xpt_scan_bus: xpt_create_path failed"
+ " with status %#x, bus scan halted\n",
+ status);
+ xpt_free_ccb(work_ccb);
+ xpt_free_ccb((union ccb *)scan_info->cpi);
+ request_ccb = scan_info->request_ccb;
+ free(scan_info, M_CAMXPT);
+ request_ccb->ccb_h.status = status;
+ xpt_done(request_ccb);
+ break;
+ }
+ xpt_setup_ccb(&work_ccb->ccb_h, path,
+ scan_info->request_ccb->ccb_h.pinfo.priority);
+ work_ccb->ccb_h.func_code = XPT_SCAN_LUN;
+ work_ccb->ccb_h.cbfcnp = mmc_scan_bus;
+ work_ccb->ccb_h.flags |= CAM_UNLOCKED;
+ work_ccb->ccb_h.ppriv_ptr0 = scan_info;
+ work_ccb->crcn.flags = scan_info->request_ccb->crcn.flags;
+ mtx_unlock(mtx);
+ xpt_action(work_ccb);
+ if (mtx != NULL)
+ mtx_lock(mtx);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+mmc_scan_lun(struct cam_periph *periph, struct cam_path *path,
+ cam_flags flags, union ccb *request_ccb)
+{
+ struct ccb_pathinq cpi;
+ cam_status status;
+ struct cam_path *new_path;
+ struct cam_periph *old_periph;
+ int lock;
+
+ CAM_DEBUG(path, CAM_DEBUG_PROBE, ("xpt_scan_lun\n"));
+
+ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+
+ if (cpi.ccb_h.status != CAM_REQ_CMP) {
+ if (request_ccb != NULL) {
+ request_ccb->ccb_h.status = cpi.ccb_h.status;
+ xpt_done(request_ccb);
+ }
+ return;
+ }
+
+ if (request_ccb == NULL) {
+ request_ccb = xpt_alloc_ccb_nowait();
+ if (request_ccb == NULL) {
+ xpt_print(path, "xpt_scan_lun: can't allocate CCB, "
+ "can't continue\n");
+ return;
+ }
+ status = xpt_create_path(&new_path, NULL,
+ path->bus->path_id,
+ path->target->target_id,
+ path->device->lun_id);
+ if (status != CAM_REQ_CMP) {
+ xpt_print(path, "xpt_scan_lun: can't create path, "
+ "can't continue\n");
+ xpt_free_ccb(request_ccb);
+ return;
+ }
+ xpt_setup_ccb(&request_ccb->ccb_h, new_path, CAM_PRIORITY_XPT);
+ request_ccb->ccb_h.cbfcnp = xptscandone;
+ request_ccb->ccb_h.flags |= CAM_UNLOCKED;
+ request_ccb->ccb_h.func_code = XPT_SCAN_LUN;
+ request_ccb->crcn.flags = flags;
+ }
+
+ lock = (xpt_path_owned(path) == 0);
+ if (lock)
+ xpt_path_lock(path);
+ if ((old_periph = cam_periph_find(path, "mmcprobe")) != NULL) {
+ xpt_print(path, " Found mmcprobe device at path\n");
+ if ((old_periph->flags & CAM_PERIPH_INVALID) == 0) {
+ xpt_print(path, " Periph is OK, insert request CCB into tailq\n");
+ } else {
+ xpt_print(path, " Old periph is invalid\n");
+ request_ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(request_ccb);
+ }
+ } else {
+ xpt_print(path, " Set up the mmcprobe device...\n");
+ status = cam_periph_alloc(mmcprobe_register, NULL,
+ mmcprobe_cleanup,
+ mmcprobe_start,
+ "mmcprobe",
+ CAM_PERIPH_BIO,
+ request_ccb->ccb_h.path, NULL, 0,
+ request_ccb);
+ request_ccb->ccb_h.status = status;
+ xpt_done(request_ccb);
+ }
+ if (lock)
+ xpt_path_unlock(path);
+}
+
+static void
+mmc_action(union ccb *start_ccb)
+{
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("mmc_action! func_code=%x, action %s\n", start_ccb->ccb_h.func_code,
+ get_action_desc(start_ccb->ccb_h.func_code)));
+ switch (start_ccb->ccb_h.func_code) {
+
+ case XPT_SCAN_BUS:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("mmc_action! XPT_SCAN_BUS\n"));
+ mmc_scan_bus(start_ccb->ccb_h.path->periph, start_ccb);
+ break;
+
+ case XPT_SCAN_TGT:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("mmc_action! XPT_SCAN_TGT\n"));
+ mmc_scan_bus(start_ccb->ccb_h.path->periph, start_ccb);
+ break;
+
+ case XPT_SCAN_LUN:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("mmc_action! XPT_SCAN_LUN\n"));
+ mmc_scan_lun(start_ccb->ccb_h.path->periph,
+ start_ccb->ccb_h.path, start_ccb->crcn.flags,
+ start_ccb);
+ break;
+
+ case XPT_DEV_ADVINFO:
+ {
+ mmc_dev_advinfo(start_ccb);
+ break;
+ }
+
+ default:
+ xpt_action_default(start_ccb);
+ break;
+ }
+}
+
+/*
+ * XXX: What's the point in having this function per-XPT?
+ */
+static void
+mmc_dev_advinfo(union ccb *start_ccb)
+{
+ struct cam_ed *device;
+ struct ccb_dev_advinfo *cdai;
+ off_t amt;
+
+ start_ccb->ccb_h.status = CAM_REQ_INVALID;
+ device = start_ccb->ccb_h.path->device;
+ cdai = &start_ccb->cdai;
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("mmc_dev_advinfo: request %x\n", cdai->buftype));
+ switch(cdai->buftype) {
+ case CDAI_TYPE_SCSI_DEVID:
+ if (cdai->flags & CDAI_FLAG_STORE)
+ return;
+ cdai->provsiz = device->device_id_len;
+ if (device->device_id_len == 0)
+ break;
+ amt = device->device_id_len;
+ if (cdai->provsiz > cdai->bufsiz)
+ amt = cdai->bufsiz;
+ memcpy(cdai->buf, device->device_id, amt);
+ break;
+ case CDAI_TYPE_SERIAL_NUM:
+ if (cdai->flags & CDAI_FLAG_STORE)
+ return;
+ cdai->provsiz = device->serial_num_len;
+ if (device->serial_num_len == 0)
+ break;
+ amt = device->serial_num_len;
+ if (cdai->provsiz > cdai->bufsiz)
+ amt = cdai->bufsiz;
+ memcpy(cdai->buf, device->serial_num, amt);
+ break;
+ case CDAI_TYPE_PHYS_PATH:
+ if (cdai->flags & CDAI_FLAG_STORE) {
+ if (device->physpath != NULL)
+ free(device->physpath, M_CAMXPT);
+ device->physpath_len = cdai->bufsiz;
+ /* Clear existing buffer if zero length */
+ if (cdai->bufsiz == 0)
+ break;
+ device->physpath = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT);
+ if (device->physpath == NULL) {
+ start_ccb->ccb_h.status = CAM_REQ_ABORTED;
+ return;
+ }
+ memcpy(device->physpath, cdai->buf, cdai->bufsiz);
+ } else {
+ cdai->provsiz = device->physpath_len;
+ if (device->physpath_len == 0)
+ break;
+ amt = device->physpath_len;
+ if (cdai->provsiz > cdai->bufsiz)
+ amt = cdai->bufsiz;
+ memcpy(cdai->buf, device->physpath, amt);
+ }
+ break;
+ default:
+ return;
+ }
+ start_ccb->ccb_h.status = CAM_REQ_CMP;
+
+ if (cdai->flags & CDAI_FLAG_STORE) {
+ xpt_async(AC_ADVINFO_CHANGED, start_ccb->ccb_h.path,
+ (void *)(uintptr_t)cdai->buftype);
+ }
+}
+
+static void
+mmc_announce_periph(struct cam_periph *periph)
+{
+ struct ccb_pathinq cpi;
+ struct ccb_trans_settings cts;
+ struct cam_path *path = periph->path;
+
+ cam_periph_assert(periph, MA_OWNED);
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+ ("mmc_announce_periph: called\n"));
+
+ xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL);
+ cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+ xpt_action((union ccb*)&cts);
+ if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ return;
+ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+ printf("SIM infromation should appear here!\n");
+}
+
+void
+mmc_print_ident(struct mmc_params *ident_data)
+{
+ printf("(Some information about this MMC/SD device here)\n");
+}
+
+static periph_init_t probe_periph_init;
+
+static struct periph_driver probe_driver =
+{
+ probe_periph_init, "mmcprobe",
+ TAILQ_HEAD_INITIALIZER(probe_driver.units), /* generation */ 0,
+ CAM_PERIPH_DRV_EARLY
+};
+
+PERIPHDRIVER_DECLARE(mmcprobe, probe_driver);
+
+#define CARD_ID_FREQUENCY 400000 /* Spec requires 400kHz max during ID phase. */
+
+typedef enum {
+ PROBE_RESET,
+ PROBE_IDENTIFY,
+ PROBE_SEND_IF_COND,
+ PROBE_SEND_APP_OP_COND_APPCMD,
+ PROBE_SEND_APP_OP_COND_ACMD41,
+ PROBE_DONE,
+ PROBE_INVALID
+} probe_action;
+
+static char *probe_action_text[] = {
+ "PROBE_RESET",
+ "PROBE_IDENTIFY",
+ "PROBE_SEND_IF_COND",
+ "PROBE_SEND_APP_OP_COND_APPCMD",
+ "PROBE_SEND_APP_OP_COND_ACMD41",
+ "PROBE_DONE",
+ "PROBE_INVALID"
+};
+
+#define PROBE_SET_ACTION(softc, newaction) \
+do { \
+ char **text; \
+ text = probe_action_text; \
+ CAM_DEBUG((softc)->periph->path, CAM_DEBUG_PROBE, \
+ ("Probe %s to %s\n", text[(softc)->action], \
+ text[(newaction)])); \
+ (softc)->action = (newaction); \
+} while(0)
+
+typedef enum {
+ PROBE_WTF = 0x01
+} probe_flags;
+
+typedef struct {
+ TAILQ_HEAD(, ccb_hdr) request_ccbs;
+ probe_action action;
+ int restart;
+ union ccb saved_ccb;
+ probe_flags flags;
+ struct cam_periph *periph;
+
+ /* MMC Bus settings */
+ uint32_t host_ocr;
+} mmcprobe_softc;
+
+static void
+probe_periph_init()
+{
+}
+
+static cam_status
+mmcprobe_register(struct cam_periph *periph, void *arg)
+{
+ union ccb *request_ccb; /* CCB representing the probe request */
+ cam_status status;
+ mmcprobe_softc *softc;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("mmcprobe_register\n"));
+
+ request_ccb = (union ccb *)arg;
+ if (request_ccb == NULL) {
+ printf("mmcprobe_register: no probe CCB, "
+ "can't register device\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ softc = (mmcprobe_softc *)malloc(sizeof(*softc), M_CAMXPT, M_NOWAIT);
+
+ if (softc == NULL) {
+ printf("proberegister: Unable to probe new device. "
+ "Unable to allocate softc\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ softc->flags = 0;
+ periph->softc = softc;
+ softc->periph = periph;
+ softc->action = PROBE_INVALID;
+ softc->restart = 0;
+ status = cam_periph_acquire(periph);
+ if (status != CAM_REQ_CMP) {
+ printf("proberegister: cam_periph_acquire failed (status=%d)\n",
+ status);
+ return (status);
+ }
+ CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe started\n"));
+/*
+ * ATA XPT then sends the following CCB to the SIM:
+ cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+...
+ * ... but now there is nothing to send yet, I think
+*/
+
+/*
+ * ATA XPT then calls probeschedule() here, but we dont have
+ * so much work to do to write a separate function :)
+*/
+ if (periph->path->device->flags & CAM_DEV_UNCONFIGURED)
+ PROBE_SET_ACTION(softc, PROBE_RESET);
+ else
+ PROBE_SET_ACTION(softc, PROBE_IDENTIFY);
+
+ /* This will kick the ball */
+ xpt_schedule(periph, CAM_PRIORITY_XPT);
+
+ return(CAM_REQ_CMP);
+}
+
+static int
+mmc_highest_voltage(uint32_t ocr)
+{
+ int i;
+
+ for (i = MMC_OCR_MAX_VOLTAGE_SHIFT;
+ i >= MMC_OCR_MIN_VOLTAGE_SHIFT; i--)
+ if (ocr & (1 << i))
+ return (i);
+ return (-1);
+}
+
+static inline void
+init_standard_ccb(union ccb *ccb, uint32_t cmd)
+{
+ ccb->ccb_h.func_code = cmd;
+ ccb->ccb_h.flags = CAM_DIR_OUT;
+ ccb->ccb_h.retry_count = 0;
+ ccb->ccb_h.timeout = 15 * 1000;
+ ccb->ccb_h.cbfcnp = mmcprobe_done;
+}
+
+static void
+mmc_build_app_op_cond(union ccb *ccb, uint32_t ocr)
+{
+ struct ccb_mmcio *mmcio;
+
+ mmcio = &ccb->mmcio;
+
+ init_standard_ccb(ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = ACMD_SD_SEND_OP_COND;
+ mmcio->cmd.arg = ocr;
+ mmcio->cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+ mmcio->cmd.data = NULL;
+}
+
+static void
+mmcprobe_start(struct cam_periph *periph, union ccb *start_ccb)
+{
+ mmcprobe_softc *softc;
+ struct cam_path *path;
+ struct ccb_mmcio *mmcio;
+ struct ccb_trans_settings_mmc *cts;
+ struct mtx *p_mtx = cam_periph_mtx(periph);
+
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("mmcprobe_start\n"));
+ softc = (mmcprobe_softc *)periph->softc;
+ path = start_ccb->ccb_h.path;
+ mmcio = &start_ccb->mmcio;
+ cts = &start_ccb->cts.proto_specific.mmc;
+
+ memset(&mmcio->cmd, 0, sizeof(struct mmc_command));
+
+ if (softc->restart) {
+ softc->restart = 0;
+ if (path->device->flags & CAM_DEV_UNCONFIGURED)
+ softc->action = PROBE_RESET;
+ else
+ softc->action = PROBE_IDENTIFY;
+
+ }
+
+ /* Here is the place where the identify fun begins */
+ switch (softc->action) {
+ case PROBE_RESET:
+ /* FALLTHROUGH */
+ case PROBE_IDENTIFY:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("Start with PROBE_RESET\n"));
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.power_mode = power_off;
+ cts->ios_valid = MMC_PM;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 100);
+
+ /* mmc_power_up */
+ /* Get the host OCR */
+ init_standard_ccb(start_ccb, XPT_GET_TRAN_SETTINGS);
+ xpt_action(start_ccb);
+
+ uint32_t hv = mmc_highest_voltage(cts->host_ocr);
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.vdd = hv;
+ cts->ios.bus_mode = opendrain;
+ cts->ios.chip_select = cs_dontcare;
+ cts->ios.power_mode = power_up;
+ cts->ios.bus_width = bus_width_1;
+ cts->ios.clock = 0;
+ cts->ios_valid = MMC_VDD | MMC_PM | MMC_BM |
+ MMC_CS | MMC_BW | MMC_CLK;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 100);
+
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.power_mode = power_on;
+ cts->ios.clock = CARD_ID_FREQUENCY;
+ cts->ios.timing = bus_timing_normal;
+ cts->ios_valid = MMC_PM | MMC_CLK | MMC_BT;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 100);
+ /* End for mmc_power_on */
+
+ /* Begin mmc_idle_cards() */
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.chip_select = cs_high;
+ cts->ios_valid = MMC_CS;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 1);
+
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_GO_IDLE_STATE;
+ mmcio->cmd.arg = 0;
+ mmcio->cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+ mmcio->cmd.data = NULL;
+ break;
+
+ case PROBE_SEND_IF_COND:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Start with PROBE_SEND_IF_COND\n"));
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = SD_SEND_IF_COND; /* CMD 8 */
+ mmcio->cmd.arg = (1 << 8) + 0xAA;
+ mmcio->cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+ mmcio->cmd.data = NULL;
+ break;
+
+ case PROBE_SEND_APP_OP_COND_APPCMD:
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_APP_CMD; /* CMD 55 */
+ mmcio->cmd.arg = 0; /* rca << 16 */
+ mmcio->cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ mmcio->cmd.data = NULL;
+ break;
+
+ case PROBE_SEND_APP_OP_COND_ACMD41:
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = ACMD_SD_SEND_OP_COND; /* CMD 55 */
+ /* XXX: Don't set HCS if no response to CMD8 */
+ mmcio->cmd.arg = 1 << 30; /* HCS + ocr */
+ mmcio->cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;;
+ mmcio->cmd.data = NULL;
+ break;
+
+ case PROBE_DONE:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("Start with PROBE_DONE\n"));
+ return;
+ /* NOTREACHED */
+ break;
+ default:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("probestart: invalid action state 0x%x\n", softc->action));
+ panic("ciao");
+ }
+
+ start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
+ xpt_action(start_ccb);
+}
+
+static void mmcprobe_cleanup(struct cam_periph *periph)
+{
+ free(periph->softc, M_CAMXPT);
+}
+
+static void
+mmcprobe_done(struct cam_periph *periph, union ccb *done_ccb)
+{
+ mmcprobe_softc *softc;
+ struct cam_path *path;
+ int found = 1;
+ int err;
+ struct ccb_mmcio *mmcio;
+ u_int32_t priority;
+
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("mmcprobe_done\n"));
+ softc = (mmcprobe_softc *)periph->softc;
+ path = done_ccb->ccb_h.path;
+ priority = done_ccb->ccb_h.pinfo.priority;
+
+ if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ /* Call generic error handler */
+ if (cam_periph_error(done_ccb,
+ 0, softc->restart ? (SF_NO_RECOVERY | SF_NO_RETRY) : 0,
+ NULL) == ERESTART) {
+ /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */
+ cam_release_devq(path, 0, 0, 0, FALSE);
+ return;
+ }
+ if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+ /* Don't wedge the queue */
+ xpt_release_devq(path, /*count*/1, /*run_queue*/TRUE);
+ }
+
+ if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0)
+ xpt_async(AC_LOST_DEVICE, path, NULL);
+ found = 0;
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+
+ xpt_release_ccb(done_ccb);
+ /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */
+ cam_release_devq(path, 0, 0, 0, FALSE);
+ cam_periph_invalidate(periph);
+ cam_periph_release_locked(periph);
+ return;
+ }
+
+ switch (softc->action) {
+ case PROBE_RESET:
+ /* FALLTHROUGH */
+ case PROBE_IDENTIFY:
+ {
+ printf("Starting completion of PROBE_RESET\n");
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("done with PROBE_RESET\n"));
+ path->device->protocol = PROTO_MMCSD;
+ PROBE_SET_ACTION(softc, PROBE_SEND_IF_COND);
+ break;
+ }
+ case PROBE_SEND_IF_COND:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ if (err != MMC_ERR_NONE || mmcio->cmd.resp[0] != 0x1AA) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("IF_COND: error %d, pattern %08x\n",
+ err, mmcio->cmd.resp[0]));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ } else {
+ PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND_APPCMD);
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("SD 2.0 interface conditions: OK\n"));
+ }
+ break;
+ }
+ case PROBE_SEND_APP_OP_COND_APPCMD:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ if (err != MMC_ERR_NONE || !(mmcio->cmd.resp[0] & R1_APP_CMD)) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("APP_OP_COND: error %d, resp %08x\n",
+ err, mmcio->cmd.resp[0]));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ } else
+ PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND_ACMD41);
+ break;
+ }
+ case PROBE_SEND_APP_OP_COND_ACMD41:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("APP_OP_COND: error %d\n", err));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ } else {
+ if ((mmcio->cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
+ (mmcio->cmd.arg & MMC_OCR_VOLTAGE) == 0) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Card OCR: %08x\n", mmcio->cmd.resp[0]));
+ PROBE_SET_ACTION(softc, PROBE_DONE);
+ } else {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Card not ready: %08x\n", mmcio->cmd.resp[0]));
+ /* Send CMD55+ACMD41 once again */
+ PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND_APPCMD);
+ }
+ }
+ break;
+ }
+ default:
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("mmc_probedone: invalid action state 0x%x\n", softc->action));
+ panic("ciao");
+ }
+
+ xpt_release_ccb(done_ccb);
+ xpt_schedule(periph, priority);
+ /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */
+ cam_release_devq(path, 0, 0, 0, FALSE);
+}
+
+
+/* Helper stuff */
+static const char *
+get_action_desc(int action)
+{
+ switch (action) {
+ case XPT_FC_QUEUED:
+ return "XPT_FC_QUEUED";
+ ;break;
+ case XPT_FC_USER_CCB:
+ return "XPT_FC_USER_CCB";
+ ;break;
+ case XPT_FC_XPT_ONLY:
+ return "XPT_FC_XPT_ONLY";
+ ;break;
+ case XPT_FC_DEV_QUEUED:
+ return "XPT_FC_DEV_QUEUED";
+ ;break;
+ case XPT_NOOP:
+ return "XPT_NOOP";
+ ;break;
+ case XPT_SCSI_IO:
+ return "XPT_SCSI_IO";
+ ;break;
+ case XPT_GDEV_TYPE:
+ return "XPT_GDEV_TYPE";
+ ;break;
+ case XPT_GDEVLIST:
+ return "XPT_GDEVLIST";
+ ;break;
+ case XPT_PATH_INQ:
+ return "XPT_PATH_INQ";
+ ;break;
+ case XPT_REL_SIMQ:
+ return "XPT_REL_SIMQ";
+ ;break;
+ case XPT_SASYNC_CB:
+ return "XPT_SASYNC_CB";
+ ;break;
+ case XPT_SDEV_TYPE:
+ return "XPT_SDEV_TYPE";
+ ;break;
+ case XPT_SCAN_BUS:
+ return "XPT_SCAN_BUS";
+ ;break;
+ case XPT_DEV_MATCH:
+ return "XPT_DEV_MATCH";
+ ;break;
+ case XPT_DEBUG:
+ return "XPT_DEBUG";
+ ;break;
+ case XPT_PATH_STATS:
+ return "XPT_PATH_STATS";
+ ;break;
+ case XPT_GDEV_STATS:
+ return "XPT_GDEV_STATS";
+ ;break;
+ case XPT_DEV_ADVINFO:
+ return "XPT_DEV_ADVINFO";
+ ;break;
+ case XPT_ASYNC:
+ return "XPT_ASYNC";
+ ;break;
+ case XPT_ABORT:
+ return "XPT_ABORT";
+ ;break;
+ case XPT_RESET_BUS:
+ return "XPT_RESET_BUS";
+ ;break;
+ case XPT_RESET_DEV:
+ return "XPT_RESET_DEV";
+ ;break;
+ case XPT_TERM_IO:
+ return "XPT_TERM_IO";
+ ;break;
+ case XPT_SCAN_LUN:
+ return "XPT_SCAN_LUN";
+ ;break;
+ case XPT_GET_TRAN_SETTINGS:
+ return "XPT_GET_TRAN_SETTINGS";
+ ;break;
+ case XPT_SET_TRAN_SETTINGS:
+ return "XPT_SET_TRAN_SETTINGS";
+ ;break;
+ case XPT_CALC_GEOMETRY:
+ return "XPT_CALC_GEOMETRY";
+ ;break;
+ case XPT_ATA_IO:
+ return "XPT_ATA_IO";
+ ;break;
+ case XPT_GET_SIM_KNOB:
+ return "XPT_GET_SIM_KNOB";
+ ;break;
+ case XPT_SET_SIM_KNOB:
+ return "XPT_SET_SIM_KNOB";
+ ;break;
+ case XPT_SMP_IO:
+ return "XPT_SMP_IO";
+ ;break;
+ case XPT_SCAN_TGT:
+ return "XPT_SCAN_TGT";
+ ;break;
+ case XPT_ENG_INQ:
+ return "XPT_ENG_INQ";
+ ;break;
+ case XPT_ENG_EXEC:
+ return "XPT_ENG_EXEC";
+ ;break;
+ case XPT_EN_LUN:
+ return "XPT_EN_LUN";
+ ;break;
+ case XPT_TARGET_IO:
+ return "XPT_TARGET_IO";
+ ;break;
+ case XPT_ACCEPT_TARGET_IO:
+ return "XPT_ACCEPT_TARGET_IO";
+ ;break;
+ case XPT_CONT_TARGET_IO:
+ return "XPT_CONT_TARGET_IO";
+ ;break;
+ case XPT_IMMED_NOTIFY:
+ return "XPT_IMMED_NOTIFY";
+ ;break;
+ case XPT_NOTIFY_ACK:
+ return "XPT_NOTIFY_ACK";
+ ;break;
+ case XPT_IMMEDIATE_NOTIFY:
+ return "XPT_IMMEDIATE_NOTIFY";
+ ;break;
+ case XPT_NOTIFY_ACKNOWLEDGE:
+ return "XPT_NOTIFY_ACKNOWLEDGE";
+ ;break;
+ case XPT_MMC_IO:
+ return "XPT_MMC_IO";
+ ;break;
+ case XPT_VUNIQUE:
+ return "XPT_VUNIQUE";
+ ;break;
+ default:
+ return "<Unknown>";
+ }
+}
Index: sys/cam/mmc/mmcreg.h
===================================================================
--- /dev/null
+++ sys/cam/mmc/mmcreg.h
@@ -0,0 +1,445 @@
+/*-
+ * Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef DEV_MMC_MMCREG_H
+#define DEV_MMC_MMCREG_H
+
+/*
+ * This file contains the register definitions for the mmc and sd busses.
+ * They are taken from publicly available sources.
+ */
+
+struct mmc_data;
+struct mmc_request;
+
+struct mmc_command {
+ uint32_t opcode;
+ uint32_t arg;
+ uint32_t resp[4];
+ uint32_t flags; /* Expected responses */
+#define MMC_RSP_PRESENT (1ul << 0) /* Response */
+#define MMC_RSP_136 (1ul << 1) /* 136 bit response */
+#define MMC_RSP_CRC (1ul << 2) /* Expect valid crc */
+#define MMC_RSP_BUSY (1ul << 3) /* Card may send busy */
+#define MMC_RSP_OPCODE (1ul << 4) /* Response include opcode */
+#define MMC_RSP_MASK 0x1ful
+#define MMC_CMD_AC (0ul << 5) /* Addressed Command, no data */
+#define MMC_CMD_ADTC (1ul << 5) /* Addressed Data transfer cmd */
+#define MMC_CMD_BC (2ul << 5) /* Broadcast command, no response */
+#define MMC_CMD_BCR (3ul << 5) /* Broadcast command with response */
+#define MMC_CMD_MASK (3ul << 5)
+
+/* Possible response types defined in the standard: */
+#define MMC_RSP_NONE (0)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY)
+#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
+#define MMC_RSP_R3 (MMC_RSP_PRESENT)
+#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC)
+#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC)
+#define MMC_RSP(x) ((x) & MMC_RSP_MASK)
+ uint32_t retries;
+ uint32_t error;
+#define MMC_ERR_NONE 0
+#define MMC_ERR_TIMEOUT 1
+#define MMC_ERR_BADCRC 2
+#define MMC_ERR_FIFO 3
+#define MMC_ERR_FAILED 4
+#define MMC_ERR_INVALID 5
+#define MMC_ERR_NO_MEMORY 6
+#define MMC_ERR_MAX 6
+ struct mmc_data *data; /* Data segment with cmd */
+ struct mmc_request *mrq; /* backpointer to request */
+};
+
+/*
+ * R1 responses
+ *
+ * Types (per SD 2.0 standard)
+ * e : error bit
+ * s : status bit
+ * r : detected and set for the actual command response
+ * x : Detected and set during command execution. The host can get
+ * the status by issuing a command with R1 response.
+ *
+ * Clear Condition (per SD 2.0 standard)
+ * a : according to the card current state.
+ * b : always related to the previous command. reception of a valid
+ * command will clear it (with a delay of one command).
+ * c : clear by read
+ */
+#define R1_OUT_OF_RANGE (1u << 31) /* erx, c */
+#define R1_ADDRESS_ERROR (1u << 30) /* erx, c */
+#define R1_BLOCK_LEN_ERROR (1u << 29) /* erx, c */
+#define R1_ERASE_SEQ_ERROR (1u << 28) /* er, c */
+#define R1_ERASE_PARAM (1u << 27) /* erx, c */
+#define R1_WP_VIOLATION (1u << 26) /* erx, c */
+#define R1_CARD_IS_LOCKED (1u << 25) /* sx, a */
+#define R1_LOCK_UNLOCK_FAILED (1u << 24) /* erx, c */
+#define R1_COM_CRC_ERROR (1u << 23) /* er, b */
+#define R1_ILLEGAL_COMMAND (1u << 22) /* er, b */
+#define R1_CARD_ECC_FAILED (1u << 21) /* erx, c */
+#define R1_CC_ERROR (1u << 20) /* erx, c */
+#define R1_ERROR (1u << 19) /* erx, c */
+#define R1_CSD_OVERWRITE (1u << 16) /* erx, c */
+#define R1_WP_ERASE_SKIP (1u << 15) /* erx, c */
+#define R1_CARD_ECC_DISABLED (1u << 14) /* sx, a */
+#define R1_ERASE_RESET (1u << 13) /* sr, c */
+#define R1_CURRENT_STATE_MASK (0xfu << 9) /* sx, b */
+#define R1_READY_FOR_DATA (1u << 8) /* sx, a */
+#define R1_APP_CMD (1u << 5) /* sr, c */
+#define R1_AKE_SEQ_ERROR (1u << 3) /* er, c */
+#define R1_STATUS(x) ((x) & 0xFFFFE000)
+#define R1_CURRENT_STATE(x) (((x) & R1_CURRENT_STATE_MASK) >> 9)
+#define R1_STATE_IDLE 0
+#define R1_STATE_READY 1
+#define R1_STATE_IDENT 2
+#define R1_STATE_STBY 3
+#define R1_STATE_TRAN 4
+#define R1_STATE_DATA 5
+#define R1_STATE_RCV 6
+#define R1_STATE_PRG 7
+#define R1_STATE_DIS 8
+
+struct mmc_data {
+ size_t len; /* size of the data */
+ size_t xfer_len;
+ void *data; /* data buffer */
+ uint32_t flags;
+#define MMC_DATA_WRITE (1UL << 0)
+#define MMC_DATA_READ (1UL << 1)
+#define MMC_DATA_STREAM (1UL << 2)
+#define MMC_DATA_MULTI (1UL << 3)
+ struct mmc_request *mrq;
+};
+
+struct mmc_request {
+ struct mmc_command *cmd;
+ struct mmc_command *stop;
+ void (*done)(struct mmc_request *); /* Completion function */
+ void *done_data; /* requestor set data */
+ uint32_t flags;
+#define MMC_REQ_DONE 1
+};
+
+/* Command definitions */
+
+/* Class 0 and 1: Basic commands & read stream commands */
+#define MMC_GO_IDLE_STATE 0
+#define MMC_SEND_OP_COND 1
+#define MMC_ALL_SEND_CID 2
+#define MMC_SET_RELATIVE_ADDR 3
+#define SD_SEND_RELATIVE_ADDR 3
+#define MMC_SET_DSR 4
+ /* reserved: 5 */
+#define MMC_SWITCH_FUNC 6
+#define MMC_SWITCH_FUNC_CMDS 0
+#define MMC_SWITCH_FUNC_SET 1
+#define MMC_SWITCH_FUNC_CLR 2
+#define MMC_SWITCH_FUNC_WR 3
+#define MMC_SELECT_CARD 7
+#define MMC_DESELECT_CARD 7
+#define MMC_SEND_EXT_CSD 8
+#define SD_SEND_IF_COND 8
+#define MMC_SEND_CSD 9
+#define MMC_SEND_CID 10
+#define MMC_READ_DAT_UNTIL_STOP 11
+#define MMC_STOP_TRANSMISSION 12
+#define MMC_SEND_STATUS 13
+#define MMC_BUSTEST_R 14
+#define MMC_GO_INACTIVE_STATE 15
+#define MMC_BUSTEST_W 19
+
+/* Class 2: Block oriented read commands */
+#define MMC_SET_BLOCKLEN 16
+#define MMC_READ_SINGLE_BLOCK 17
+#define MMC_READ_MULTIPLE_BLOCK 18
+ /* reserved: 19 */
+
+/* Class 3: Stream write commands */
+#define MMC_WRITE_DAT_UNTIL_STOP 20
+ /* reserved: 21 */
+ /* reserved: 22 */
+
+/* Class 4: Block oriented write commands */
+#define MMC_SET_BLOCK_COUNT 23
+#define MMC_WRITE_BLOCK 24
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_PROGARM_CID 26
+#define MMC_PROGRAM_CSD 27
+
+/* Class 6: Block oriented write protection commands */
+#define MMC_SET_WRITE_PROT 28
+#define MMC_CLR_WRITE_PROT 29
+#define MMC_SEND_WRITE_PROT 30
+ /* reserved: 31 */
+
+/* Class 5: Erase commands */
+#define SD_ERASE_WR_BLK_START 32
+#define SD_ERASE_WR_BLK_END 33
+ /* 34 -- reserved old command */
+#define MMC_ERASE_GROUP_START 35
+#define MMC_ERASE_GROUP_END 36
+ /* 37 -- reserved old command */
+#define MMC_ERASE 38
+
+/* Class 9: I/O mode commands */
+#define MMC_FAST_IO 39
+#define MMC_GO_IRQ_STATE 40
+ /* reserved: 41 */
+
+/* Class 7: Lock card */
+#define MMC_LOCK_UNLOCK 42
+ /* reserved: 43 */
+ /* reserved: 44 */
+ /* reserved: 45 */
+ /* reserved: 46 */
+ /* reserved: 47 */
+ /* reserved: 48 */
+ /* reserved: 49 */
+ /* reserved: 50 */
+ /* reserved: 51 */
+ /* reserved: 54 */
+
+/* Class 8: Application specific commands */
+#define MMC_APP_CMD 55
+#define MMC_GEN_CMD 56
+ /* reserved: 57 */
+ /* reserved: 58 */
+ /* reserved: 59 */
+ /* reserved for mfg: 60 */
+ /* reserved for mfg: 61 */
+ /* reserved for mfg: 62 */
+ /* reserved for mfg: 63 */
+
+/* Class 9: I/O cards (sd) */
+#define SD_IO_RW_DIRECT 52
+#define SD_IO_RW_EXTENDED 53
+
+/* Class 10: Switch function commands */
+#define SD_SWITCH_FUNC 6
+ /* reserved: 34 */
+ /* reserved: 35 */
+ /* reserved: 36 */
+ /* reserved: 37 */
+ /* reserved: 50 */
+ /* reserved: 57 */
+
+
+/* Application specific commands for SD */
+#define ACMD_SET_BUS_WIDTH 6
+#define ACMD_SD_STATUS 13
+#define ACMD_SEND_NUM_WR_BLOCKS 22
+#define ACMD_SET_WR_BLK_ERASE_COUNT 23
+#define ACMD_SD_SEND_OP_COND 41
+#define ACMD_SET_CLR_CARD_DETECT 42
+#define ACMD_SEND_SCR 51
+
+/*
+ * EXT_CSD fields
+ */
+#define EXT_CSD_ERASE_GRP_DEF 175 /* R/W */
+#define EXT_CSD_BUS_WIDTH 183 /* R/W */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_REV 192 /* RO */
+#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+#define EXT_CSD_ERASE_TO_MULT 223 /* RO */
+#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */
+
+/*
+ * EXT_CSD field definitions
+ */
+#define EXT_CSD_CMD_SET_NORMAL 1
+#define EXT_CSD_CMD_SET_SECURE 2
+#define EXT_CSD_CMD_SET_CPSECURE 4
+
+#define EXT_CSD_CARD_TYPE_26 1
+#define EXT_CSD_CARD_TYPE_52 2
+
+#define EXT_CSD_BUS_WIDTH_1 0
+#define EXT_CSD_BUS_WIDTH_4 1
+#define EXT_CSD_BUS_WIDTH_8 2
+
+#define MMC_TYPE_26_MAX_HS 26000000
+#define MMC_TYPE_52_MAX_HS 52000000
+
+/*
+ * SD bus widths
+ */
+#define SD_BUS_WIDTH_1 0
+#define SD_BUS_WIDTH_4 2
+
+/*
+ * SD Switch
+ */
+#define SD_SWITCH_MODE_CHECK 0
+#define SD_SWITCH_MODE_SET 1
+#define SD_SWITCH_GROUP1 0
+#define SD_SWITCH_NORMAL_MODE 0
+#define SD_SWITCH_HS_MODE 1
+#define SD_SWITCH_NOCHANGE 0xF
+
+#define SD_CLR_CARD_DETECT 0
+#define SD_SET_CARD_DETECT 1
+
+#define SD_MAX_HS 50000000
+
+/* OCR bits */
+
+/*
+ * in SD 2.0 spec, bits 8-14 are now marked reserved
+ * Low voltage in SD2.0 spec is bit 7, TBD voltage
+ * Low voltage in MC 3.31 spec is bit 7, 1.65-1.95V
+ * Specs prior to MMC 3.31 defined bits 0-7 as voltages down to 1.5V.
+ * 3.31 redefined them to be reserved and also said that cards had to
+ * support the 2.7-3.6V and fixed the OCR to be 0xfff8000 for high voltage
+ * cards. MMC 4.0 says that a dual voltage card responds with 0xfff8080.
+ * Looks like the fine-grained control of the voltage tolerance ranges
+ * was abandoned.
+ *
+ * The MMC_OCR_CCS appears to be valid for only SD cards.
+ */
+#define MMC_OCR_VOLTAGE 0x3fffffffU /* Vdd Voltage mask */
+#define MMC_OCR_LOW_VOLTAGE (1u << 7) /* Low Voltage Range -- tbd */
+#define MMC_OCR_200_210 (1U << 8) /* Vdd voltage 2.00 ~ 2.10 */
+#define MMC_OCR_MIN_VOLTAGE_SHIFT 8
+#define MMC_OCR_210_220 (1U << 9) /* Vdd voltage 2.10 ~ 2.20 */
+#define MMC_OCR_220_230 (1U << 10) /* Vdd voltage 2.20 ~ 2.30 */
+#define MMC_OCR_230_240 (1U << 11) /* Vdd voltage 2.30 ~ 2.40 */
+#define MMC_OCR_240_250 (1U << 12) /* Vdd voltage 2.40 ~ 2.50 */
+#define MMC_OCR_250_260 (1U << 13) /* Vdd voltage 2.50 ~ 2.60 */
+#define MMC_OCR_260_270 (1U << 14) /* Vdd voltage 2.60 ~ 2.70 */
+#define MMC_OCR_270_280 (1U << 15) /* Vdd voltage 2.70 ~ 2.80 */
+#define MMC_OCR_280_290 (1U << 16) /* Vdd voltage 2.80 ~ 2.90 */
+#define MMC_OCR_290_300 (1U << 17) /* Vdd voltage 2.90 ~ 3.00 */
+#define MMC_OCR_300_310 (1U << 18) /* Vdd voltage 3.00 ~ 3.10 */
+#define MMC_OCR_310_320 (1U << 19) /* Vdd voltage 3.10 ~ 3.20 */
+#define MMC_OCR_320_330 (1U << 20) /* Vdd voltage 3.20 ~ 3.30 */
+#define MMC_OCR_330_340 (1U << 21) /* Vdd voltage 3.30 ~ 3.40 */
+#define MMC_OCR_340_350 (1U << 22) /* Vdd voltage 3.40 ~ 3.50 */
+#define MMC_OCR_350_360 (1U << 23) /* Vdd voltage 3.50 ~ 3.60 */
+#define MMC_OCR_MAX_VOLTAGE_SHIFT 23
+#define MMC_OCR_CCS (1u << 30) /* Card Capacity status (SD vs SDHC) */
+#define MMC_OCR_CARD_BUSY (1U << 31) /* Card Power up status */
+
+/* CSD -- decoded structure */
+struct mmc_cid {
+ uint32_t mid;
+ char pnm[8];
+ uint32_t psn;
+ uint16_t oid;
+ uint16_t mdt_year;
+ uint8_t mdt_month;
+ uint8_t prv;
+ uint8_t fwrev;
+};
+
+struct mmc_csd
+{
+ uint8_t csd_structure;
+ uint8_t spec_vers;
+ uint16_t ccc;
+ uint16_t tacc;
+ uint32_t nsac;
+ uint32_t r2w_factor;
+ uint32_t tran_speed;
+ uint32_t read_bl_len;
+ uint32_t write_bl_len;
+ uint32_t vdd_r_curr_min;
+ uint32_t vdd_r_curr_max;
+ uint32_t vdd_w_curr_min;
+ uint32_t vdd_w_curr_max;
+ uint32_t wp_grp_size;
+ uint32_t erase_sector;
+ uint64_t capacity;
+ unsigned int read_bl_partial:1,
+ read_blk_misalign:1,
+ write_bl_partial:1,
+ write_blk_misalign:1,
+ dsr_imp:1,
+ erase_blk_en:1,
+ wp_grp_enable:1;
+};
+
+struct mmc_scr
+{
+ unsigned char sda_vsn;
+ unsigned char bus_widths;
+#define SD_SCR_BUS_WIDTH_1 (1<<0)
+#define SD_SCR_BUS_WIDTH_4 (1<<2)
+};
+
+struct mmc_sd_status
+{
+ uint8_t bus_width;
+ uint8_t secured_mode;
+ uint16_t card_type;
+ uint16_t prot_area;
+ uint8_t speed_class;
+ uint8_t perf_move;
+ uint8_t au_size;
+ uint16_t erase_size;
+ uint8_t erase_timeout;
+ uint8_t erase_offset;
+};
+
+/*
+ * Older versions of the MMC standard had a variable sector size. However,
+ * I've been able to find no old MMC or SD cards that have a non 512
+ * byte sector size anywhere, so we assume that such cards are very rare
+ * and only note their existance in passing here...
+ */
+#define MMC_SECTOR_SIZE 512
+
+#endif /* DEV_MMCREG_H */
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -91,6 +91,7 @@
cam/ctl/ctl_error.c optional ctl
cam/ctl/ctl_util.c optional ctl
cam/ctl/scsi_ctl.c optional ctl
+cam/mmc/mmc_xpt.c optional scbus
cam/scsi/scsi_da.c optional da
cam/scsi/scsi_low.c optional ct | ncv | nsp | stg
cam/scsi/scsi_pass.c optional pass
@@ -1879,6 +1880,7 @@
dev/mmc/mmcbr_if.m standard
dev/mmc/mmcbus_if.m standard
dev/mmc/mmcsd.c optional mmcsd
+dev/mmcnull/mmcnull.c optional mmcnull
dev/mn/if_mn.c optional mn pci
dev/mpr/mpr.c optional mpr
dev/mpr/mpr_config.c optional mpr
Index: sys/dev/mmcnull/Makefile
===================================================================
--- /dev/null
+++ sys/dev/mmcnull/Makefile
@@ -0,0 +1,4 @@
+KMOD= mmcnull
+SRCS= mmcnull.c
+
+.include <bsd.kmod.mk>
Index: sys/dev/mmcnull/mmcnull.c
===================================================================
--- /dev/null
+++ sys/dev/mmcnull/mmcnull.c
@@ -0,0 +1,328 @@
+/*-
+ * Copyright (c) 2013 Ilya Bakulin. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/scsi/scsi_all.h>
+
+struct mmcnull_softc {
+ device_t dev;
+ struct mtx sc_mtx;
+
+ struct cam_devq *devq;
+ struct cam_sim *sim;
+ struct cam_path *path;
+
+ struct callout tick;
+ union ccb *cur_ccb;
+};
+
+static void mmcnull_identify(driver_t *, device_t);
+static int mmcnull_probe(device_t);
+static int mmcnull_attach(device_t);
+static int mmcnull_detach(device_t);
+static void mmcnull_action(struct cam_sim *, union ccb *);
+static void mmcnull_poll(struct cam_sim *);
+
+static void
+mmcnull_callout(void *arg)
+{
+ struct mmcnull_softc *sc;
+
+ sc = (struct mmcnull_softc *)arg;
+
+ callout_reset(&sc->tick, 1, mmcnull_callout, sc);
+}
+
+static void
+mmcnull_identify(driver_t *driver, device_t parent)
+{
+ device_t child;
+
+ if (resource_disabled("mmcnull", 0))
+ return;
+
+ if (device_get_unit(parent) != 0)
+ return;
+
+ /* Avoid duplicates. */
+ if (device_find_child(parent, "mmcnull", -1))
+ return;
+
+ child = BUS_ADD_CHILD(parent, 20, "mmcnull", 0);
+ if (child == NULL) {
+ device_printf(parent, "add MMCNULL child failed\n");
+ return;
+ }
+}
+
+
+static int
+mmcnull_probe(device_t dev)
+{
+ device_set_desc(dev, "Emulated MMC controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mmcnull_attach(device_t dev)
+{
+ struct mmcnull_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ mtx_init(&sc->sc_mtx, "mmcnullmtx", NULL, MTX_DEF);
+
+ if ((sc->devq = cam_simq_alloc(1)) == NULL)
+ return (ENOMEM);
+
+ sc->sim = cam_sim_alloc(mmcnull_action, mmcnull_poll, "mmcnull", sc,
+ device_get_unit(dev), &sc->sc_mtx, 1, 1,
+ sc->devq);
+
+ if (sc->sim == NULL) {
+ cam_simq_free(sc->devq);
+ device_printf(dev, "cannot allocate CAM SIM\n");
+ return (EINVAL);
+ }
+
+ mtx_lock(&sc->sc_mtx);
+ if (xpt_bus_register(sc->sim, dev, 0) != 0) {
+ device_printf(dev,
+ "cannot register SCSI pass-through bus\n");
+ cam_sim_free(sc->sim, FALSE);
+ cam_simq_free(sc->devq);
+ mtx_unlock(&sc->sc_mtx);
+ return (EINVAL);
+ }
+ mtx_unlock(&sc->sc_mtx);
+
+ callout_init_mtx(&sc->tick, &sc->sc_mtx, 0); /* Callout to emulate interrupts */
+
+ device_printf(dev, "attached OK\n");
+
+ return (0);
+}
+
+static int
+mmcnull_detach(device_t dev)
+{
+ struct mmcnull_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc == NULL)
+ return (EINVAL);
+
+ if (sc->sim != NULL) {
+ mtx_lock(&sc->sc_mtx);
+ xpt_bus_deregister(cam_sim_path(sc->sim));
+ cam_sim_free(sc->sim, FALSE);
+ mtx_unlock(&sc->sc_mtx);
+ }
+
+ if (sc->devq != NULL)
+ cam_simq_free(sc->devq);
+
+ callout_drain(&sc->tick);
+ mtx_destroy(&sc->sc_mtx);
+
+ device_printf(dev, "detached OK\n");
+ return (0);
+}
+
+/*
+ * The interrupt handler
+ * This implementation calls it via callout(9)
+ * with the mutex already taken
+ */
+static void
+mmcnull_intr(void *xsc)
+{
+ struct mmcnull_softc *sc;
+ union ccb *ccb;
+ struct ccb_mmcio *mmcio;
+
+ sc = (struct mmcnull_softc *) xsc;
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ ccb = sc->cur_ccb;
+ mmcio = &ccb->mmcio;
+ device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n",
+ mmcio->mmc_command);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+
+ sc->cur_ccb = NULL;
+ xpt_done(ccb);
+}
+
+/*
+ * This is a MMC IO handler
+ * It extracts MMC command from CCB and sends it
+ * to the h/w
+ */
+static void
+mmcnull_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
+{
+ struct mmcnull_softc *sc;
+ struct ccb_mmcio *mmcio;
+
+ sc = cam_sim_softc(sim);
+ mmcio = &ccb->mmcio;
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ sc->cur_ccb = ccb;
+
+ /* Real h/w will wait for the interrupt */
+ callout_reset(&sc->tick, hz * 10, mmcnull_intr, sc);
+}
+
+static void
+mmcnull_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct mmcnull_softc *sc;
+
+ sc = cam_sim_softc(sim);
+ if (sc == NULL) {
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ xpt_done(ccb);
+ return;
+ }
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ device_printf(sc->dev, "action: func_code %0x\n", ccb->ccb_h.func_code);
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi;
+
+ cpi = &ccb->cpi;
+ cpi->version_num = 1;
+ cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_16;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = 0;
+ cpi->max_lun = 0;
+ cpi->initiator_id = 1;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Deglitch Networks", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 100; /* XXX WTF? */
+ cpi->protocol = PROTO_MMCSD;
+ cpi->protocol_version = SCSI_REV_0;
+ cpi->transport = XPORT_MMCSD;
+ cpi->transport_version = 0;
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ device_printf(sc->dev, "Got XPT_GET_TRAN_SETTINGS\n");
+
+ cts->protocol = PROTO_MMCSD;
+ cts->protocol_version = 0;
+ cts->transport = XPORT_MMCSD;
+ cts->transport_version = 0;
+ cts->xport_specific.valid = 0;
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ device_printf(sc->dev, "Got XPT_SET_TRAN_SETTINGS, should update IOS...\n");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_RESET_BUS:
+ device_printf(sc->dev, "Got XPT_RESET_BUS, ACK it...\n");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_MMC_IO:
+ /*
+ * Here is the HW-dependent part of
+ * sending the command to the underlying h/w
+ * At some point in the future an interrupt comes.
+ * Then the request will be marked as completed.
+ */
+ device_printf(sc->dev, "Got XPT_MMC_IO\n");
+ mmcnull_handle_mmcio(sim, ccb);
+ return;
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+ return;
+}
+
+static void
+mmcnull_poll(struct cam_sim *sim)
+{
+ return;
+}
+
+
+static device_method_t mmcnull_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, mmcnull_identify),
+ DEVMETHOD(device_probe, mmcnull_probe),
+ DEVMETHOD(device_attach, mmcnull_attach),
+ DEVMETHOD(device_detach, mmcnull_detach),
+ DEVMETHOD_END
+};
+
+static driver_t mmcnull_driver = {
+ "mmcnull", mmcnull_methods, sizeof(struct mmcnull_softc)
+};
+
+static devclass_t mmcnull_devclass;
+
+DRIVER_MODULE(mmcnull, cpu, mmcnull_driver, mmcnull_devclass, 0, 0);
Index: sys/dev/sdhci/sdhci.h
===================================================================
--- sys/dev/sdhci/sdhci.h
+++ sys/dev/sdhci/sdhci.h
@@ -247,7 +247,10 @@
struct mmc_host host; /* Host parameters */
struct mmc_request *req; /* Current request */
struct mmc_command *curcmd; /* Current command of current request */
-
+
+ /* CAM stuff */
+ union ccb *ccb;
+
uint32_t intmask; /* Current interrupt mask */
uint32_t clock; /* Current clock freq. */
size_t offset; /* Data buffer offset */
@@ -281,4 +284,7 @@
void sdhci_generic_intr(struct sdhci_slot *slot);
uint32_t sdhci_generic_min_freq(device_t brdev, struct sdhci_slot *slot);
+/* CAM-related */
+int sdhci_update_ios_cam(struct sdhci_slot *slot);
+int sdhci_request_cam(struct sdhci_slot *slot, union ccb *ccb);
#endif /* __SDHCI_H__ */
Index: sys/dev/sdhci/sdhci.c
===================================================================
--- sys/dev/sdhci/sdhci.c
+++ sys/dev/sdhci/sdhci.c
@@ -48,13 +48,19 @@
#include <dev/mmc/mmcreg.h>
#include <dev/mmc/mmcbrvar.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+
#include "mmcbr_if.h"
#include "sdhci.h"
#include "sdhci_if.h"
SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver");
-static int sdhci_debug;
+static int sdhci_debug = 10;
SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RWTUN, &sdhci_debug, 0, "Debug level");
#define RD1(slot, off) SDHCI_READ_1((slot)->bus, (slot), (off))
@@ -470,6 +476,7 @@
int err;
SDHCI_LOCK_INIT(slot);
+
slot->num = num;
slot->bus = dev;
@@ -599,6 +606,7 @@
TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot);
callout_init(&slot->card_callout, 1);
callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0);
+
return (0);
}
@@ -661,12 +669,23 @@
return (slot->max_clk / SDHCI_200_MAX_DIVIDER);
}
+/*
+ * When called from the old stack:
+ sdhci_ti0: This is a bridge device
+ mmc0: This is a request device
+ sdhci_ti0-slot0: <--- The locking slot is this
+ *
+ */
int
sdhci_generic_update_ios(device_t brdev, device_t reqdev)
{
struct sdhci_slot *slot = device_get_ivars(reqdev);
struct mmc_ios *ios = &slot->host.ios;
+ device_printf(brdev, "This is a bridge device\n");
+ device_printf(reqdev, "This is a request device\n");
+
+ slot_printf(slot, " <--- The locking slot is this\n");
SDHCI_LOCK(slot);
/* Do full reset on bus power down to clear from any state. */
if (ios->power_mode == power_off) {
@@ -767,6 +786,9 @@
if ((state & SDHCI_CARD_PRESENT) == 0 ||
slot->power == 0 ||
slot->clock == 0) {
+ slot_printf(slot,
+ "Cannot issue a command (card=%d power=%d clock=%d)",
+ state, slot->power, slot->clock);
cmd->error = MMC_ERR_FAILED;
sdhci_req_done(slot);
return;
@@ -777,8 +799,9 @@
if (cmd->data || (cmd->flags & MMC_RSP_BUSY))
mask |= SDHCI_DAT_INHIBIT;
/* We shouldn't wait for DAT for stop commands. */
- if (cmd == slot->req->stop)
+/* if (cmd == slot->req->stop)
mask &= ~SDHCI_DAT_INHIBIT;
+*/
/*
* Wait for bus no more then 250 ms. Typically there will be no wait
* here at all, but when writing a crash dump we may be bypassing the
@@ -836,6 +859,7 @@
WR4(slot, SDHCI_ARGUMENT, cmd->arg);
/* Set data transfer mode. */
sdhci_set_transfer_mode(slot, cmd->data);
+ slot_printf(slot, "Starting command!\n");
/* Start command. */
WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
/* Start timeout callout. */
@@ -847,6 +871,8 @@
{
int i;
+ slot_printf(slot, "%s: called, err %d flags %d\n",
+ __func__, slot->curcmd->error, slot->curcmd->flags);
slot->cmd_done = 1;
/* Interrupt aggregation: Restore command interrupt.
* Main restore point for the case when command interrupt
@@ -877,6 +903,10 @@
} else
slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE);
}
+ printf("Resp: %02x %02x %02x %02x\n",
+ slot->curcmd->resp[0], slot->curcmd->resp[1],
+ slot->curcmd->resp[2], slot->curcmd->resp[3]);
+
/* If data ready - finish. */
if (slot->data_done)
sdhci_start(slot);
@@ -1300,6 +1330,11 @@
}
SDHCI_UNLOCK(slot);
+
+ /* Tell CAM the request is finished */
+ slot->ccb->ccb_h.status = CAM_REQ_CMP;
+
+ xpt_done(slot->ccb);
}
int
@@ -1361,6 +1396,7 @@
{
struct sdhci_slot *slot = device_get_ivars(child);
+ slot_printf(slot, "sdhci_generic_write_ivar, var=%d\n", which);
switch (which) {
default:
return (EINVAL);
@@ -1428,4 +1464,75 @@
return (0);
}
+/* CAM-related functions */
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+
+int
+sdhci_update_ios_cam(struct sdhci_slot *slot)
+{
+ struct mmc_ios *ios = &slot->host.ios;
+
+ slot_printf(slot, " <--- The locking slot is this\n");
+ SDHCI_LOCK(slot);
+ /* Do full reset on bus power down to clear from any state. */
+ if (ios->power_mode == power_off) {
+ WR4(slot, SDHCI_SIGNAL_ENABLE, 0);
+ sdhci_init(slot);
+ }
+ /* Configure the bus. */
+ sdhci_set_clock(slot, ios->clock);
+ sdhci_set_power(slot, (ios->power_mode == power_off)?0:ios->vdd);
+ if (ios->bus_width == bus_width_4)
+ slot->hostctrl |= SDHCI_CTRL_4BITBUS;
+ else
+ slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
+ if (ios->timing == bus_timing_hs)
+ slot->hostctrl |= SDHCI_CTRL_HISPD;
+ else
+ slot->hostctrl &= ~SDHCI_CTRL_HISPD;
+ WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
+ /* Some controllers like reset after bus changes. */
+ if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS)
+ sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ SDHCI_UNLOCK(slot);
+ return (0);
+}
+
+int
+sdhci_request_cam(struct sdhci_slot *slot, union ccb *ccb)
+{
+ struct ccb_mmcio *mmcio;
+
+ mmcio = &ccb->mmcio;
+
+ SDHCI_LOCK(slot);
+ if (slot->req != NULL) {
+ SDHCI_UNLOCK(slot);
+ return (EBUSY);
+ }
+ if (sdhci_debug > 1) {
+ slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
+ mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags,
+ (mmcio->cmd.data)?(u_int)mmcio->cmd.data->len:0,
+ (mmcio->cmd.data)?mmcio->cmd.data->flags:0);
+ }
+// slot->req = req;
+ slot->ccb = ccb;
+ slot->flags = 0;
+ sdhci_start_command(slot, &mmcio->cmd);
+ SDHCI_UNLOCK(slot);
+ if (dumping) {
+ while (slot->req != NULL) {
+ sdhci_generic_intr(slot);
+ DELAY(10);
+ }
+ }
+ return (0);
+}
+
MODULE_VERSION(sdhci, 1);
Index: sys/modules/mmcnull/Makefile
===================================================================
--- /dev/null
+++ sys/modules/mmcnull/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/mmcnull
+
+KMOD= mmcnull
+SRCS= mmcnull.c device_if.h bus_if.h
+
+.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Apr 19, 1:24 AM (21 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31708226
Default Alt Text
D862.id1806.diff (90 KB)
Attached To
Mode
D862: RFC: MMC stack under CAM
Attached
Detach File
Event Timeline
Log In to Comment