xref: /freebsd/sys/dev/mmcnull/mmcnull.c (revision b58c5abf075fcbfc515a353113470374ffc95b6f)
11b99d52fSWarner Losh /*-
21b99d52fSWarner Losh  * Copyright (c) 2013 Ilya Bakulin.  All rights reserved.
31b99d52fSWarner Losh  *
41b99d52fSWarner Losh  * Redistribution and use in source and binary forms, with or without
51b99d52fSWarner Losh  * modification, are permitted provided that the following conditions
61b99d52fSWarner Losh  * are met:
71b99d52fSWarner Losh  * 1. Redistributions of source code must retain the above copyright
81b99d52fSWarner Losh  *    notice, this list of conditions and the following disclaimer.
91b99d52fSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
101b99d52fSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
111b99d52fSWarner Losh  *    documentation and/or other materials provided with the distribution.
121b99d52fSWarner Losh  *
131b99d52fSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
141b99d52fSWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
151b99d52fSWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
161b99d52fSWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
171b99d52fSWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
181b99d52fSWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
191b99d52fSWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
201b99d52fSWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
211b99d52fSWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
221b99d52fSWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
231b99d52fSWarner Losh  *
241b99d52fSWarner Losh  */
251b99d52fSWarner Losh 
261b99d52fSWarner Losh #include <sys/cdefs.h>
271b99d52fSWarner Losh __FBSDID("$FreeBSD$");
281b99d52fSWarner Losh 
291b99d52fSWarner Losh #include <sys/param.h>
301b99d52fSWarner Losh #include <sys/module.h>
311b99d52fSWarner Losh #include <sys/kernel.h>
321b99d52fSWarner Losh #include <sys/malloc.h>
331b99d52fSWarner Losh #include <sys/systm.h>
341b99d52fSWarner Losh #include <sys/lock.h>
351b99d52fSWarner Losh #include <sys/mutex.h>
361b99d52fSWarner Losh #include <sys/bus.h>
371b99d52fSWarner Losh #include <sys/endian.h>
381b99d52fSWarner Losh #include <sys/sysctl.h>
391b99d52fSWarner Losh 
401b99d52fSWarner Losh #include <cam/cam.h>
411b99d52fSWarner Losh #include <cam/cam_ccb.h>
421b99d52fSWarner Losh #include <cam/cam_debug.h>
431b99d52fSWarner Losh #include <cam/cam_sim.h>
441b99d52fSWarner Losh #include <cam/cam_xpt_sim.h>
451b99d52fSWarner Losh #include <cam/scsi/scsi_all.h>
461b99d52fSWarner Losh 
471b99d52fSWarner Losh static int is_sdio_mode = 1;
481b99d52fSWarner Losh 
491b99d52fSWarner Losh struct mmcnull_softc {
501b99d52fSWarner Losh 	device_t dev;
511b99d52fSWarner Losh 	struct mtx sc_mtx;
521b99d52fSWarner Losh 
531b99d52fSWarner Losh 	struct cam_devq		*devq;
541b99d52fSWarner Losh 	struct cam_sim		*sim;
551b99d52fSWarner Losh 	struct cam_path		*path;
561b99d52fSWarner Losh 
571b99d52fSWarner Losh 	struct callout		 tick;
581b99d52fSWarner Losh 	union ccb		*cur_ccb;
591b99d52fSWarner Losh };
601b99d52fSWarner Losh 
611b99d52fSWarner Losh static void mmcnull_identify(driver_t *, device_t);
621b99d52fSWarner Losh static int  mmcnull_probe(device_t);
631b99d52fSWarner Losh static int  mmcnull_attach(device_t);
641b99d52fSWarner Losh static int  mmcnull_detach(device_t);
651b99d52fSWarner Losh static void mmcnull_action_sd(struct cam_sim *, union ccb *);
661b99d52fSWarner Losh static void mmcnull_action_sdio(struct cam_sim *, union ccb *);
671b99d52fSWarner Losh static void mmcnull_intr_sd(void *xsc);
681b99d52fSWarner Losh static void mmcnull_intr_sdio(void *xsc);
691b99d52fSWarner Losh static void mmcnull_poll(struct cam_sim *);
701b99d52fSWarner Losh 
711b99d52fSWarner Losh static void
721b99d52fSWarner Losh mmcnull_identify(driver_t *driver, device_t parent)
731b99d52fSWarner Losh {
741b99d52fSWarner Losh 	device_t child;
751b99d52fSWarner Losh 
761b99d52fSWarner Losh 	if (resource_disabled("mmcnull", 0))
771b99d52fSWarner Losh 		return;
781b99d52fSWarner Losh 
791b99d52fSWarner Losh 	if (device_get_unit(parent) != 0)
801b99d52fSWarner Losh 		return;
811b99d52fSWarner Losh 
821b99d52fSWarner Losh 	/* Avoid duplicates. */
831b99d52fSWarner Losh 	if (device_find_child(parent, "mmcnull", -1))
841b99d52fSWarner Losh 		return;
851b99d52fSWarner Losh 
861b99d52fSWarner Losh 	child = BUS_ADD_CHILD(parent, 20, "mmcnull", 0);
871b99d52fSWarner Losh 	if (child == NULL) {
881b99d52fSWarner Losh 		device_printf(parent, "add MMCNULL child failed\n");
891b99d52fSWarner Losh 		return;
901b99d52fSWarner Losh 	}
911b99d52fSWarner Losh }
921b99d52fSWarner Losh 
931b99d52fSWarner Losh 
941b99d52fSWarner Losh static int
951b99d52fSWarner Losh mmcnull_probe(device_t dev)
961b99d52fSWarner Losh {
971b99d52fSWarner Losh 	device_set_desc(dev, "Emulated MMC controller");
981b99d52fSWarner Losh 	return (BUS_PROBE_DEFAULT);
991b99d52fSWarner Losh }
1001b99d52fSWarner Losh 
1011b99d52fSWarner Losh static int
1021b99d52fSWarner Losh mmcnull_attach(device_t dev)
1031b99d52fSWarner Losh {
1041b99d52fSWarner Losh 	struct mmcnull_softc *sc;
1051b99d52fSWarner Losh 	sim_action_func action_func;
1061b99d52fSWarner Losh 
1071b99d52fSWarner Losh 	sc = device_get_softc(dev);
1081b99d52fSWarner Losh 	sc->dev = dev;
1091b99d52fSWarner Losh 
1101b99d52fSWarner Losh 	mtx_init(&sc->sc_mtx, "mmcnullmtx", NULL, MTX_DEF);
1111b99d52fSWarner Losh 
1121b99d52fSWarner Losh 	if ((sc->devq = cam_simq_alloc(1)) == NULL)
1131b99d52fSWarner Losh 		return (ENOMEM);
1141b99d52fSWarner Losh 
1151b99d52fSWarner Losh 	if (is_sdio_mode)
1161b99d52fSWarner Losh 		action_func = mmcnull_action_sdio;
1171b99d52fSWarner Losh 	else
1181b99d52fSWarner Losh 		action_func = mmcnull_action_sd;
1191b99d52fSWarner Losh 	sc->sim = cam_sim_alloc(action_func, mmcnull_poll, "mmcnull", sc,
1201b99d52fSWarner Losh 				device_get_unit(dev), &sc->sc_mtx, 1, 1,
1211b99d52fSWarner Losh 				sc->devq);
1221b99d52fSWarner Losh 
1231b99d52fSWarner Losh 	if (sc->sim == NULL) {
1241b99d52fSWarner Losh 		cam_simq_free(sc->devq);
1251b99d52fSWarner Losh 		device_printf(dev, "cannot allocate CAM SIM\n");
1261b99d52fSWarner Losh 		return (EINVAL);
1271b99d52fSWarner Losh 	}
1281b99d52fSWarner Losh 
1291b99d52fSWarner Losh 	mtx_lock(&sc->sc_mtx);
1301b99d52fSWarner Losh 	if (xpt_bus_register(sc->sim, dev, 0) != 0) {
1311b99d52fSWarner Losh 		device_printf(dev,
1321b99d52fSWarner Losh 			      "cannot register SCSI pass-through bus\n");
1331b99d52fSWarner Losh 		cam_sim_free(sc->sim, FALSE);
1341b99d52fSWarner Losh 		cam_simq_free(sc->devq);
1351b99d52fSWarner Losh 		mtx_unlock(&sc->sc_mtx);
1361b99d52fSWarner Losh 		return (EINVAL);
1371b99d52fSWarner Losh 	}
1381b99d52fSWarner Losh 	mtx_unlock(&sc->sc_mtx);
1391b99d52fSWarner Losh 
1401b99d52fSWarner Losh 	callout_init_mtx(&sc->tick, &sc->sc_mtx, 0);	/* Callout to emulate interrupts */
1411b99d52fSWarner Losh 
1421b99d52fSWarner Losh 	device_printf(dev, "attached OK\n");
1431b99d52fSWarner Losh 
1441b99d52fSWarner Losh 	return (0);
1451b99d52fSWarner Losh }
1461b99d52fSWarner Losh 
1471b99d52fSWarner Losh static int
1481b99d52fSWarner Losh mmcnull_detach(device_t dev)
1491b99d52fSWarner Losh {
1501b99d52fSWarner Losh 	struct mmcnull_softc *sc;
1511b99d52fSWarner Losh 
1521b99d52fSWarner Losh 	sc = device_get_softc(dev);
1531b99d52fSWarner Losh 
1541b99d52fSWarner Losh 	if (sc == NULL)
1551b99d52fSWarner Losh 		return (EINVAL);
1561b99d52fSWarner Losh 
1571b99d52fSWarner Losh 	if (sc->sim != NULL) {
1581b99d52fSWarner Losh 		mtx_lock(&sc->sc_mtx);
1591b99d52fSWarner Losh 		xpt_bus_deregister(cam_sim_path(sc->sim));
1601b99d52fSWarner Losh 		cam_sim_free(sc->sim, FALSE);
1611b99d52fSWarner Losh 		mtx_unlock(&sc->sc_mtx);
1621b99d52fSWarner Losh 	}
1631b99d52fSWarner Losh 
1641b99d52fSWarner Losh 	if (sc->devq != NULL)
1651b99d52fSWarner Losh 		cam_simq_free(sc->devq);
1661b99d52fSWarner Losh 
1671b99d52fSWarner Losh 	callout_drain(&sc->tick);
1681b99d52fSWarner Losh 	mtx_destroy(&sc->sc_mtx);
1691b99d52fSWarner Losh 
1701b99d52fSWarner Losh 	device_printf(dev, "detached OK\n");
1711b99d52fSWarner Losh 	return (0);
1721b99d52fSWarner Losh }
1731b99d52fSWarner Losh 
1741b99d52fSWarner Losh /*
1751b99d52fSWarner Losh  * The interrupt handler
1761b99d52fSWarner Losh  * This implementation calls it via callout(9)
1771b99d52fSWarner Losh  * with the mutex already taken
1781b99d52fSWarner Losh  */
1791b99d52fSWarner Losh static void
1801b99d52fSWarner Losh mmcnull_intr_sd(void *xsc) {
1811b99d52fSWarner Losh 	struct mmcnull_softc *sc;
1821b99d52fSWarner Losh 	union ccb *ccb;
1831b99d52fSWarner Losh 	struct ccb_mmcio *mmcio;
1841b99d52fSWarner Losh 
1851b99d52fSWarner Losh 	sc = (struct mmcnull_softc *) xsc;
1861b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
1871b99d52fSWarner Losh 
1881b99d52fSWarner Losh 	ccb = sc->cur_ccb;
1891b99d52fSWarner Losh 	mmcio = &ccb->mmcio;
1901b99d52fSWarner Losh 	device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n",
1911b99d52fSWarner Losh 		      mmcio->cmd.opcode);
1921b99d52fSWarner Losh 
1931b99d52fSWarner Losh 	switch (mmcio->cmd.opcode) {
1941b99d52fSWarner Losh 	case MMC_GO_IDLE_STATE:
1951b99d52fSWarner Losh 		device_printf(sc->dev, "Reset device\n");
1961b99d52fSWarner Losh 		break;
1971b99d52fSWarner Losh 	case SD_SEND_IF_COND:
1981b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1AA; // To match mmc_xpt expectations :-)
1991b99d52fSWarner Losh 		break;
2001b99d52fSWarner Losh 	case MMC_APP_CMD:
2011b99d52fSWarner Losh 		mmcio->cmd.resp[0] = R1_APP_CMD;
2021b99d52fSWarner Losh 		break;
2031b99d52fSWarner Losh 	case SD_SEND_RELATIVE_ADDR:
2041b99d52fSWarner Losh 	case MMC_SELECT_CARD:
2051b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1 << 16;
2061b99d52fSWarner Losh 		break;
2071b99d52fSWarner Losh 	case ACMD_SD_SEND_OP_COND:
2081b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0xc0ff8000;
2091b99d52fSWarner Losh 		mmcio->cmd.resp[0] |= MMC_OCR_CARD_BUSY;
2101b99d52fSWarner Losh 		break;
2111b99d52fSWarner Losh 	case MMC_ALL_SEND_CID:
2121b99d52fSWarner Losh 		/* Note: this is a real CID from Wandboard int mmc */
2131b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1b534d30;
2141b99d52fSWarner Losh 		mmcio->cmd.resp[1] = 0x30303030;
2151b99d52fSWarner Losh 		mmcio->cmd.resp[2] = 0x10842806;
2161b99d52fSWarner Losh 		mmcio->cmd.resp[3] = 0x5700e900;
2171b99d52fSWarner Losh 		break;
2181b99d52fSWarner Losh 	case MMC_SEND_CSD:
2191b99d52fSWarner Losh 		/* Note: this is a real CSD from Wandboard int mmc */
2201b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x400e0032;
2211b99d52fSWarner Losh 		mmcio->cmd.resp[1] = 0x5b590000;
2221b99d52fSWarner Losh 		mmcio->cmd.resp[2] = 0x751f7f80;
2231b99d52fSWarner Losh 		mmcio->cmd.resp[3] = 0x0a404000;
2241b99d52fSWarner Losh 		break;
2251b99d52fSWarner Losh 	case MMC_READ_SINGLE_BLOCK:
2261b99d52fSWarner Losh 	case MMC_READ_MULTIPLE_BLOCK:
2271b99d52fSWarner Losh 		strcpy(mmcio->cmd.data->data, "WTF?!");
2281b99d52fSWarner Losh 		break;
2291b99d52fSWarner Losh 	default:
2301b99d52fSWarner Losh 		device_printf(sc->dev, "mmcnull_intr_sd: unknown command\n");
2311b99d52fSWarner Losh 		mmcio->cmd.error = 1;
2321b99d52fSWarner Losh 	}
2331b99d52fSWarner Losh 	ccb->ccb_h.status = CAM_REQ_CMP;
2341b99d52fSWarner Losh 
2351b99d52fSWarner Losh 	sc->cur_ccb = NULL;
2361b99d52fSWarner Losh 	xpt_done(ccb);
2371b99d52fSWarner Losh }
2381b99d52fSWarner Losh 
2391b99d52fSWarner Losh static void
2401b99d52fSWarner Losh mmcnull_intr_sdio_newintr(void *xsc) {
2411b99d52fSWarner Losh 	struct mmcnull_softc *sc;
2421b99d52fSWarner Losh 	struct cam_path *dpath;
2431b99d52fSWarner Losh 
2441b99d52fSWarner Losh 	sc = (struct mmcnull_softc *) xsc;
2451b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
2461b99d52fSWarner Losh 	device_printf(sc->dev, "mmcnull_intr_sdio_newintr()\n");
2471b99d52fSWarner Losh 
2481b99d52fSWarner Losh 	/* Our path */
2491b99d52fSWarner Losh 	if (xpt_create_path(&dpath, NULL, cam_sim_path(sc->sim), 0, 0) != CAM_REQ_CMP) {
2501b99d52fSWarner Losh 		device_printf(sc->dev, "mmcnull_intr_sdio_newintr(): cannot create path\n");
2511b99d52fSWarner Losh 		return;
2521b99d52fSWarner Losh 	}
2531b99d52fSWarner Losh 	xpt_async(AC_UNIT_ATTENTION, dpath, NULL);
2541b99d52fSWarner Losh 	xpt_free_path(dpath);
2551b99d52fSWarner Losh }
2561b99d52fSWarner Losh 
2571b99d52fSWarner Losh static void
2581b99d52fSWarner Losh mmcnull_intr_sdio(void *xsc) {
2591b99d52fSWarner Losh 	struct mmcnull_softc *sc;
2601b99d52fSWarner Losh 	union ccb *ccb;
2611b99d52fSWarner Losh 	struct ccb_mmcio *mmcio;
2621b99d52fSWarner Losh 
2631b99d52fSWarner Losh 	sc = (struct mmcnull_softc *) xsc;
2641b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
2651b99d52fSWarner Losh 
2661b99d52fSWarner Losh 	ccb = sc->cur_ccb;
2671b99d52fSWarner Losh 	mmcio = &ccb->mmcio;
2681b99d52fSWarner Losh 	device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n",
2691b99d52fSWarner Losh 		      mmcio->cmd.opcode);
2701b99d52fSWarner Losh 
2711b99d52fSWarner Losh 	switch (mmcio->cmd.opcode) {
2721b99d52fSWarner Losh 	case MMC_GO_IDLE_STATE:
2731b99d52fSWarner Losh 		device_printf(sc->dev, "Reset device\n");
2741b99d52fSWarner Losh 		break;
2751b99d52fSWarner Losh 	case SD_SEND_IF_COND:
2761b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1AA; // To match mmc_xpt expectations :-)
2771b99d52fSWarner Losh 		break;
2781b99d52fSWarner Losh 	case MMC_APP_CMD:
2791b99d52fSWarner Losh 		mmcio->cmd.resp[0] = R1_APP_CMD;
2801b99d52fSWarner Losh 		break;
2811b99d52fSWarner Losh 	case IO_SEND_OP_COND:
2821b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x12345678;
2831b99d52fSWarner Losh 		mmcio->cmd.resp[0] |= ~ R4_IO_MEM_PRESENT;
2841b99d52fSWarner Losh 		break;
2851b99d52fSWarner Losh 	case SD_SEND_RELATIVE_ADDR:
2861b99d52fSWarner Losh 	case MMC_SELECT_CARD:
2871b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1 << 16;
2881b99d52fSWarner Losh 		break;
2891b99d52fSWarner Losh 	case ACMD_SD_SEND_OP_COND:
2901b99d52fSWarner Losh 		/* TODO: steal valid OCR from somewhere :-) */
2911b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x123;
2921b99d52fSWarner Losh 		mmcio->cmd.resp[0] |= MMC_OCR_CARD_BUSY;
2931b99d52fSWarner Losh 		break;
2941b99d52fSWarner Losh 	case MMC_ALL_SEND_CID:
2951b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1234;
2961b99d52fSWarner Losh 		mmcio->cmd.resp[1] = 0x5678;
2971b99d52fSWarner Losh 		mmcio->cmd.resp[2] = 0x9ABC;
2981b99d52fSWarner Losh 		mmcio->cmd.resp[3] = 0xDEF0;
2991b99d52fSWarner Losh 		break;
3001b99d52fSWarner Losh 	case MMC_READ_SINGLE_BLOCK:
3011b99d52fSWarner Losh 	case MMC_READ_MULTIPLE_BLOCK:
3021b99d52fSWarner Losh 		strcpy(mmcio->cmd.data->data, "WTF?!");
3031b99d52fSWarner Losh 		break;
3041b99d52fSWarner Losh 	case SD_IO_RW_DIRECT:
3051b99d52fSWarner Losh 		device_printf(sc->dev, "Scheduling interrupt generation...\n");
3061b99d52fSWarner Losh 		callout_reset(&sc->tick, hz / 10, mmcnull_intr_sdio_newintr, sc);
3071b99d52fSWarner Losh 		break;
3081b99d52fSWarner Losh 	default:
3091b99d52fSWarner Losh 		device_printf(sc->dev, "mmcnull_intr_sdio: unknown command\n");
3101b99d52fSWarner Losh 	}
3111b99d52fSWarner Losh 	ccb->ccb_h.status = CAM_REQ_CMP;
3121b99d52fSWarner Losh 
3131b99d52fSWarner Losh 	sc->cur_ccb = NULL;
3141b99d52fSWarner Losh 	xpt_done(ccb);
3151b99d52fSWarner Losh }
3161b99d52fSWarner Losh 
3171b99d52fSWarner Losh /*
3181b99d52fSWarner Losh  * This is a MMC IO handler
3191b99d52fSWarner Losh  * It extracts MMC command from CCB and sends it
3201b99d52fSWarner Losh  * to the h/w
3211b99d52fSWarner Losh  */
3221b99d52fSWarner Losh static void
3231b99d52fSWarner Losh mmcnull_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
3241b99d52fSWarner Losh {
3251b99d52fSWarner Losh 	struct mmcnull_softc *sc;
3261b99d52fSWarner Losh 	struct ccb_mmcio *mmcio;
3271b99d52fSWarner Losh 
3281b99d52fSWarner Losh 	sc = cam_sim_softc(sim);
3291b99d52fSWarner Losh 	mmcio = &ccb->mmcio;
3301b99d52fSWarner Losh 	ccb->ccb_h.status = CAM_REQ_INPROG;
3311b99d52fSWarner Losh 	sc->cur_ccb = ccb;
3321b99d52fSWarner Losh 
3331b99d52fSWarner Losh 	/* Real h/w will wait for the interrupt */
3341b99d52fSWarner Losh 	if (is_sdio_mode)
3351b99d52fSWarner Losh 		callout_reset(&sc->tick, hz / 10, mmcnull_intr_sdio, sc);
3361b99d52fSWarner Losh 	else
3371b99d52fSWarner Losh 		callout_reset(&sc->tick, hz / 10, mmcnull_intr_sd, sc);
3381b99d52fSWarner Losh }
3391b99d52fSWarner Losh 
3401b99d52fSWarner Losh static void
3411b99d52fSWarner Losh mmcnull_action_sd(struct cam_sim *sim, union ccb *ccb)
3421b99d52fSWarner Losh {
3431b99d52fSWarner Losh 	struct mmcnull_softc *sc;
3441b99d52fSWarner Losh 
3451b99d52fSWarner Losh 	sc = cam_sim_softc(sim);
3461b99d52fSWarner Losh 	if (sc == NULL) {
3471b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
3481b99d52fSWarner Losh 		xpt_done(ccb);
3491b99d52fSWarner Losh 		return;
3501b99d52fSWarner Losh 	}
3511b99d52fSWarner Losh 
3521b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
3531b99d52fSWarner Losh 
3541b99d52fSWarner Losh 	device_printf(sc->dev, "action: func_code %0x\n", ccb->ccb_h.func_code);
3551b99d52fSWarner Losh 
3561b99d52fSWarner Losh 	switch (ccb->ccb_h.func_code) {
3571b99d52fSWarner Losh 	case XPT_PATH_INQ:
3581b99d52fSWarner Losh 	{
3591b99d52fSWarner Losh 		struct ccb_pathinq *cpi;
3601b99d52fSWarner Losh 
3611b99d52fSWarner Losh 		cpi = &ccb->cpi;
3621b99d52fSWarner Losh 		cpi->version_num = 1;
3631b99d52fSWarner Losh 		cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_16;
3641b99d52fSWarner Losh 		cpi->target_sprt = 0;
3651b99d52fSWarner Losh 		cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
3661b99d52fSWarner Losh 		cpi->hba_eng_cnt = 0;
3671b99d52fSWarner Losh 		cpi->max_target = 0;
3681b99d52fSWarner Losh 		cpi->max_lun = 0;
3691b99d52fSWarner Losh 		cpi->initiator_id = 1;
3701b99d52fSWarner Losh 		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
3711b99d52fSWarner Losh 		strncpy(cpi->hba_vid, "FreeBSD Foundation", HBA_IDLEN);
3721b99d52fSWarner Losh 		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
3731b99d52fSWarner Losh 		cpi->unit_number = cam_sim_unit(sim);
3741b99d52fSWarner Losh 		cpi->bus_id = cam_sim_bus(sim);
3751b99d52fSWarner Losh 		cpi->base_transfer_speed = 100; /* XXX WTF? */
3761b99d52fSWarner Losh 		cpi->protocol = PROTO_MMCSD;
3771b99d52fSWarner Losh 		cpi->protocol_version = SCSI_REV_0;
3781b99d52fSWarner Losh 		cpi->transport = XPORT_MMCSD;
3791b99d52fSWarner Losh 		cpi->transport_version = 0;
3801b99d52fSWarner Losh 
3811b99d52fSWarner Losh 		cpi->ccb_h.status = CAM_REQ_CMP;
3821b99d52fSWarner Losh 		break;
3831b99d52fSWarner Losh 	}
3841b99d52fSWarner Losh 	case XPT_GET_TRAN_SETTINGS:
3851b99d52fSWarner Losh 	{
3861b99d52fSWarner Losh 		struct ccb_trans_settings *cts = &ccb->cts;
3871b99d52fSWarner Losh 		struct ccb_trans_settings_mmc *mcts;
3881b99d52fSWarner Losh 		mcts = &ccb->cts.proto_specific.mmc;
3891b99d52fSWarner Losh 
3901b99d52fSWarner Losh                 device_printf(sc->dev, "Got XPT_GET_TRAN_SETTINGS\n");
3911b99d52fSWarner Losh 
3921b99d52fSWarner Losh                 cts->protocol = PROTO_MMCSD;
3931b99d52fSWarner Losh                 cts->protocol_version = 0;
3941b99d52fSWarner Losh                 cts->transport = XPORT_MMCSD;
3951b99d52fSWarner Losh                 cts->transport_version = 0;
3961b99d52fSWarner Losh                 cts->xport_specific.valid = 0;
3971b99d52fSWarner Losh 		mcts->host_f_max = 12000000;
3981b99d52fSWarner Losh 		mcts->host_f_min = 200000;
3991b99d52fSWarner Losh 		mcts->host_ocr = 1; /* Fix this */
4001b99d52fSWarner Losh                 ccb->ccb_h.status = CAM_REQ_CMP;
4011b99d52fSWarner Losh                 break;
4021b99d52fSWarner Losh         }
4031b99d52fSWarner Losh 	case XPT_SET_TRAN_SETTINGS:
4041b99d52fSWarner Losh 		device_printf(sc->dev, "Got XPT_SET_TRAN_SETTINGS, should update IOS...\n");
4051b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_CMP;
4061b99d52fSWarner Losh 		break;
4071b99d52fSWarner Losh 	case XPT_RESET_BUS:
4081b99d52fSWarner Losh 		device_printf(sc->dev, "Got XPT_RESET_BUS, ACK it...\n");
4091b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_CMP;
4101b99d52fSWarner Losh                 break;
4111b99d52fSWarner Losh 	case XPT_MMC_IO:
4121b99d52fSWarner Losh 		/*
4131b99d52fSWarner Losh                  * Here is the HW-dependent part of
4141b99d52fSWarner Losh 		 * sending the command to the underlying h/w
4151b99d52fSWarner Losh 		 * At some point in the future an interrupt comes.
4161b99d52fSWarner Losh 		 * Then the request will be marked as completed.
4171b99d52fSWarner Losh 		 */
4181b99d52fSWarner Losh 		device_printf(sc->dev, "Got XPT_MMC_IO\n");
4191b99d52fSWarner Losh 		mmcnull_handle_mmcio(sim, ccb);
4201b99d52fSWarner Losh 		return;
4211b99d52fSWarner Losh 		break;
4221b99d52fSWarner Losh         case XPT_RESET_DEV:
4231b99d52fSWarner Losh                 /* This is sent by `camcontrol reset`*/
4241b99d52fSWarner Losh                 device_printf(sc->dev, "Got XPT_RESET_DEV\n");
4251b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_CMP;
4261b99d52fSWarner Losh                 break;
4271b99d52fSWarner Losh 	default:
4281b99d52fSWarner Losh 		device_printf(sc->dev, "Func code %d is unknown\n", ccb->ccb_h.func_code);
4291b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_INVALID;
4301b99d52fSWarner Losh 		break;
4311b99d52fSWarner Losh 	}
4321b99d52fSWarner Losh 	xpt_done(ccb);
4331b99d52fSWarner Losh 	return;
4341b99d52fSWarner Losh }
4351b99d52fSWarner Losh 
4361b99d52fSWarner Losh static void
4371b99d52fSWarner Losh mmcnull_action_sdio(struct cam_sim *sim, union ccb *ccb) {
4381b99d52fSWarner Losh 	mmcnull_action_sd(sim, ccb);
4391b99d52fSWarner Losh }
4401b99d52fSWarner Losh 
4411b99d52fSWarner Losh static void
4421b99d52fSWarner Losh mmcnull_poll(struct cam_sim *sim)
4431b99d52fSWarner Losh {
4441b99d52fSWarner Losh 	return;
4451b99d52fSWarner Losh }
4461b99d52fSWarner Losh 
4471b99d52fSWarner Losh 
4481b99d52fSWarner Losh static device_method_t mmcnull_methods[] = {
4491b99d52fSWarner Losh 	/* Device interface */
4501b99d52fSWarner Losh 	DEVMETHOD(device_identify,      mmcnull_identify),
4511b99d52fSWarner Losh 	DEVMETHOD(device_probe,         mmcnull_probe),
4521b99d52fSWarner Losh 	DEVMETHOD(device_attach,        mmcnull_attach),
4531b99d52fSWarner Losh 	DEVMETHOD(device_detach,        mmcnull_detach),
4541b99d52fSWarner Losh 	DEVMETHOD_END
4551b99d52fSWarner Losh };
4561b99d52fSWarner Losh 
4571b99d52fSWarner Losh static driver_t mmcnull_driver = {
4581b99d52fSWarner Losh 	"mmcnull", mmcnull_methods, sizeof(struct mmcnull_softc)
4591b99d52fSWarner Losh };
4601b99d52fSWarner Losh 
461*b58c5abfSJohn Baldwin DRIVER_MODULE(mmcnull, isa, mmcnull_driver, 0, 0);
462