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