xref: /freebsd/sys/dev/mmcnull/mmcnull.c (revision 1b99d52f261bfacfb9bb149d33ed6444364ac219)
1*1b99d52fSWarner Losh /*-
2*1b99d52fSWarner Losh  * Copyright (c) 2013 Ilya Bakulin.  All rights reserved.
3*1b99d52fSWarner Losh  *
4*1b99d52fSWarner Losh  * Redistribution and use in source and binary forms, with or without
5*1b99d52fSWarner Losh  * modification, are permitted provided that the following conditions
6*1b99d52fSWarner Losh  * are met:
7*1b99d52fSWarner Losh  * 1. Redistributions of source code must retain the above copyright
8*1b99d52fSWarner Losh  *    notice, this list of conditions and the following disclaimer.
9*1b99d52fSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
10*1b99d52fSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
11*1b99d52fSWarner Losh  *    documentation and/or other materials provided with the distribution.
12*1b99d52fSWarner Losh  *
13*1b99d52fSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14*1b99d52fSWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15*1b99d52fSWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16*1b99d52fSWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17*1b99d52fSWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18*1b99d52fSWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19*1b99d52fSWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20*1b99d52fSWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21*1b99d52fSWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22*1b99d52fSWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*1b99d52fSWarner Losh  *
24*1b99d52fSWarner Losh  */
25*1b99d52fSWarner Losh 
26*1b99d52fSWarner Losh #include <sys/cdefs.h>
27*1b99d52fSWarner Losh __FBSDID("$FreeBSD$");
28*1b99d52fSWarner Losh 
29*1b99d52fSWarner Losh #include <sys/param.h>
30*1b99d52fSWarner Losh #include <sys/module.h>
31*1b99d52fSWarner Losh #include <sys/kernel.h>
32*1b99d52fSWarner Losh #include <sys/malloc.h>
33*1b99d52fSWarner Losh #include <sys/systm.h>
34*1b99d52fSWarner Losh #include <sys/lock.h>
35*1b99d52fSWarner Losh #include <sys/mutex.h>
36*1b99d52fSWarner Losh #include <sys/bus.h>
37*1b99d52fSWarner Losh #include <sys/endian.h>
38*1b99d52fSWarner Losh #include <sys/sysctl.h>
39*1b99d52fSWarner Losh 
40*1b99d52fSWarner Losh #include <cam/cam.h>
41*1b99d52fSWarner Losh #include <cam/cam_ccb.h>
42*1b99d52fSWarner Losh #include <cam/cam_debug.h>
43*1b99d52fSWarner Losh #include <cam/cam_sim.h>
44*1b99d52fSWarner Losh #include <cam/cam_xpt_sim.h>
45*1b99d52fSWarner Losh #include <cam/scsi/scsi_all.h>
46*1b99d52fSWarner Losh 
47*1b99d52fSWarner Losh static int is_sdio_mode = 1;
48*1b99d52fSWarner Losh 
49*1b99d52fSWarner Losh struct mmcnull_softc {
50*1b99d52fSWarner Losh 	device_t dev;
51*1b99d52fSWarner Losh 	struct mtx sc_mtx;
52*1b99d52fSWarner Losh 
53*1b99d52fSWarner Losh 	struct cam_devq		*devq;
54*1b99d52fSWarner Losh 	struct cam_sim		*sim;
55*1b99d52fSWarner Losh 	struct cam_path		*path;
56*1b99d52fSWarner Losh 
57*1b99d52fSWarner Losh 	struct callout		 tick;
58*1b99d52fSWarner Losh 	union ccb		*cur_ccb;
59*1b99d52fSWarner Losh };
60*1b99d52fSWarner Losh 
61*1b99d52fSWarner Losh static void mmcnull_identify(driver_t *, device_t);
62*1b99d52fSWarner Losh static int  mmcnull_probe(device_t);
63*1b99d52fSWarner Losh static int  mmcnull_attach(device_t);
64*1b99d52fSWarner Losh static int  mmcnull_detach(device_t);
65*1b99d52fSWarner Losh static void mmcnull_action_sd(struct cam_sim *, union ccb *);
66*1b99d52fSWarner Losh static void mmcnull_action_sdio(struct cam_sim *, union ccb *);
67*1b99d52fSWarner Losh static void mmcnull_intr_sd(void *xsc);
68*1b99d52fSWarner Losh static void mmcnull_intr_sdio(void *xsc);
69*1b99d52fSWarner Losh static void mmcnull_poll(struct cam_sim *);
70*1b99d52fSWarner Losh 
71*1b99d52fSWarner Losh static void
72*1b99d52fSWarner Losh mmcnull_identify(driver_t *driver, device_t parent)
73*1b99d52fSWarner Losh {
74*1b99d52fSWarner Losh 	device_t child;
75*1b99d52fSWarner Losh 
76*1b99d52fSWarner Losh 	if (resource_disabled("mmcnull", 0))
77*1b99d52fSWarner Losh 		return;
78*1b99d52fSWarner Losh 
79*1b99d52fSWarner Losh 	if (device_get_unit(parent) != 0)
80*1b99d52fSWarner Losh 		return;
81*1b99d52fSWarner Losh 
82*1b99d52fSWarner Losh 	/* Avoid duplicates. */
83*1b99d52fSWarner Losh 	if (device_find_child(parent, "mmcnull", -1))
84*1b99d52fSWarner Losh 		return;
85*1b99d52fSWarner Losh 
86*1b99d52fSWarner Losh 	child = BUS_ADD_CHILD(parent, 20, "mmcnull", 0);
87*1b99d52fSWarner Losh 	if (child == NULL) {
88*1b99d52fSWarner Losh 		device_printf(parent, "add MMCNULL child failed\n");
89*1b99d52fSWarner Losh 		return;
90*1b99d52fSWarner Losh 	}
91*1b99d52fSWarner Losh }
92*1b99d52fSWarner Losh 
93*1b99d52fSWarner Losh 
94*1b99d52fSWarner Losh static int
95*1b99d52fSWarner Losh mmcnull_probe(device_t dev)
96*1b99d52fSWarner Losh {
97*1b99d52fSWarner Losh 	device_set_desc(dev, "Emulated MMC controller");
98*1b99d52fSWarner Losh 	return (BUS_PROBE_DEFAULT);
99*1b99d52fSWarner Losh }
100*1b99d52fSWarner Losh 
101*1b99d52fSWarner Losh static int
102*1b99d52fSWarner Losh mmcnull_attach(device_t dev)
103*1b99d52fSWarner Losh {
104*1b99d52fSWarner Losh 	struct mmcnull_softc *sc;
105*1b99d52fSWarner Losh 	sim_action_func action_func;
106*1b99d52fSWarner Losh 
107*1b99d52fSWarner Losh 	sc = device_get_softc(dev);
108*1b99d52fSWarner Losh 	sc->dev = dev;
109*1b99d52fSWarner Losh 
110*1b99d52fSWarner Losh 	mtx_init(&sc->sc_mtx, "mmcnullmtx", NULL, MTX_DEF);
111*1b99d52fSWarner Losh 
112*1b99d52fSWarner Losh 	if ((sc->devq = cam_simq_alloc(1)) == NULL)
113*1b99d52fSWarner Losh 		return (ENOMEM);
114*1b99d52fSWarner Losh 
115*1b99d52fSWarner Losh 	if (is_sdio_mode)
116*1b99d52fSWarner Losh 		action_func = mmcnull_action_sdio;
117*1b99d52fSWarner Losh 	else
118*1b99d52fSWarner Losh 		action_func = mmcnull_action_sd;
119*1b99d52fSWarner Losh 	sc->sim = cam_sim_alloc(action_func, mmcnull_poll, "mmcnull", sc,
120*1b99d52fSWarner Losh 				device_get_unit(dev), &sc->sc_mtx, 1, 1,
121*1b99d52fSWarner Losh 				sc->devq);
122*1b99d52fSWarner Losh 
123*1b99d52fSWarner Losh 	if (sc->sim == NULL) {
124*1b99d52fSWarner Losh 		cam_simq_free(sc->devq);
125*1b99d52fSWarner Losh 		device_printf(dev, "cannot allocate CAM SIM\n");
126*1b99d52fSWarner Losh 		return (EINVAL);
127*1b99d52fSWarner Losh 	}
128*1b99d52fSWarner Losh 
129*1b99d52fSWarner Losh 	mtx_lock(&sc->sc_mtx);
130*1b99d52fSWarner Losh 	if (xpt_bus_register(sc->sim, dev, 0) != 0) {
131*1b99d52fSWarner Losh 		device_printf(dev,
132*1b99d52fSWarner Losh 			      "cannot register SCSI pass-through bus\n");
133*1b99d52fSWarner Losh 		cam_sim_free(sc->sim, FALSE);
134*1b99d52fSWarner Losh 		cam_simq_free(sc->devq);
135*1b99d52fSWarner Losh 		mtx_unlock(&sc->sc_mtx);
136*1b99d52fSWarner Losh 		return (EINVAL);
137*1b99d52fSWarner Losh 	}
138*1b99d52fSWarner Losh 	mtx_unlock(&sc->sc_mtx);
139*1b99d52fSWarner Losh 
140*1b99d52fSWarner Losh 	callout_init_mtx(&sc->tick, &sc->sc_mtx, 0);	/* Callout to emulate interrupts */
141*1b99d52fSWarner Losh 
142*1b99d52fSWarner Losh 	device_printf(dev, "attached OK\n");
143*1b99d52fSWarner Losh 
144*1b99d52fSWarner Losh 	return (0);
145*1b99d52fSWarner Losh }
146*1b99d52fSWarner Losh 
147*1b99d52fSWarner Losh static int
148*1b99d52fSWarner Losh mmcnull_detach(device_t dev)
149*1b99d52fSWarner Losh {
150*1b99d52fSWarner Losh 	struct mmcnull_softc *sc;
151*1b99d52fSWarner Losh 
152*1b99d52fSWarner Losh 	sc = device_get_softc(dev);
153*1b99d52fSWarner Losh 
154*1b99d52fSWarner Losh 	if (sc == NULL)
155*1b99d52fSWarner Losh 		return (EINVAL);
156*1b99d52fSWarner Losh 
157*1b99d52fSWarner Losh 	if (sc->sim != NULL) {
158*1b99d52fSWarner Losh 		mtx_lock(&sc->sc_mtx);
159*1b99d52fSWarner Losh 		xpt_bus_deregister(cam_sim_path(sc->sim));
160*1b99d52fSWarner Losh 		cam_sim_free(sc->sim, FALSE);
161*1b99d52fSWarner Losh 		mtx_unlock(&sc->sc_mtx);
162*1b99d52fSWarner Losh 	}
163*1b99d52fSWarner Losh 
164*1b99d52fSWarner Losh 	if (sc->devq != NULL)
165*1b99d52fSWarner Losh 		cam_simq_free(sc->devq);
166*1b99d52fSWarner Losh 
167*1b99d52fSWarner Losh 	callout_drain(&sc->tick);
168*1b99d52fSWarner Losh 	mtx_destroy(&sc->sc_mtx);
169*1b99d52fSWarner Losh 
170*1b99d52fSWarner Losh 	device_printf(dev, "detached OK\n");
171*1b99d52fSWarner Losh 	return (0);
172*1b99d52fSWarner Losh }
173*1b99d52fSWarner Losh 
174*1b99d52fSWarner Losh /*
175*1b99d52fSWarner Losh  * The interrupt handler
176*1b99d52fSWarner Losh  * This implementation calls it via callout(9)
177*1b99d52fSWarner Losh  * with the mutex already taken
178*1b99d52fSWarner Losh  */
179*1b99d52fSWarner Losh static void
180*1b99d52fSWarner Losh mmcnull_intr_sd(void *xsc) {
181*1b99d52fSWarner Losh 	struct mmcnull_softc *sc;
182*1b99d52fSWarner Losh 	union ccb *ccb;
183*1b99d52fSWarner Losh 	struct ccb_mmcio *mmcio;
184*1b99d52fSWarner Losh 
185*1b99d52fSWarner Losh 	sc = (struct mmcnull_softc *) xsc;
186*1b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
187*1b99d52fSWarner Losh 
188*1b99d52fSWarner Losh 	ccb = sc->cur_ccb;
189*1b99d52fSWarner Losh 	mmcio = &ccb->mmcio;
190*1b99d52fSWarner Losh 	device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n",
191*1b99d52fSWarner Losh 		      mmcio->cmd.opcode);
192*1b99d52fSWarner Losh 
193*1b99d52fSWarner Losh 	switch (mmcio->cmd.opcode) {
194*1b99d52fSWarner Losh 	case MMC_GO_IDLE_STATE:
195*1b99d52fSWarner Losh 		device_printf(sc->dev, "Reset device\n");
196*1b99d52fSWarner Losh 		break;
197*1b99d52fSWarner Losh 	case SD_SEND_IF_COND:
198*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1AA; // To match mmc_xpt expectations :-)
199*1b99d52fSWarner Losh 		break;
200*1b99d52fSWarner Losh 	case MMC_APP_CMD:
201*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = R1_APP_CMD;
202*1b99d52fSWarner Losh 		break;
203*1b99d52fSWarner Losh 	case SD_SEND_RELATIVE_ADDR:
204*1b99d52fSWarner Losh 	case MMC_SELECT_CARD:
205*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1 << 16;
206*1b99d52fSWarner Losh 		break;
207*1b99d52fSWarner Losh 	case ACMD_SD_SEND_OP_COND:
208*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0xc0ff8000;
209*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] |= MMC_OCR_CARD_BUSY;
210*1b99d52fSWarner Losh 		break;
211*1b99d52fSWarner Losh 	case MMC_ALL_SEND_CID:
212*1b99d52fSWarner Losh 		/* Note: this is a real CID from Wandboard int mmc */
213*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1b534d30;
214*1b99d52fSWarner Losh 		mmcio->cmd.resp[1] = 0x30303030;
215*1b99d52fSWarner Losh 		mmcio->cmd.resp[2] = 0x10842806;
216*1b99d52fSWarner Losh 		mmcio->cmd.resp[3] = 0x5700e900;
217*1b99d52fSWarner Losh 		break;
218*1b99d52fSWarner Losh 	case MMC_SEND_CSD:
219*1b99d52fSWarner Losh 		/* Note: this is a real CSD from Wandboard int mmc */
220*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x400e0032;
221*1b99d52fSWarner Losh 		mmcio->cmd.resp[1] = 0x5b590000;
222*1b99d52fSWarner Losh 		mmcio->cmd.resp[2] = 0x751f7f80;
223*1b99d52fSWarner Losh 		mmcio->cmd.resp[3] = 0x0a404000;
224*1b99d52fSWarner Losh 		break;
225*1b99d52fSWarner Losh 	case MMC_READ_SINGLE_BLOCK:
226*1b99d52fSWarner Losh 	case MMC_READ_MULTIPLE_BLOCK:
227*1b99d52fSWarner Losh 		strcpy(mmcio->cmd.data->data, "WTF?!");
228*1b99d52fSWarner Losh 		break;
229*1b99d52fSWarner Losh 	default:
230*1b99d52fSWarner Losh 		device_printf(sc->dev, "mmcnull_intr_sd: unknown command\n");
231*1b99d52fSWarner Losh 		mmcio->cmd.error = 1;
232*1b99d52fSWarner Losh 	}
233*1b99d52fSWarner Losh 	ccb->ccb_h.status = CAM_REQ_CMP;
234*1b99d52fSWarner Losh 
235*1b99d52fSWarner Losh 	sc->cur_ccb = NULL;
236*1b99d52fSWarner Losh 	xpt_done(ccb);
237*1b99d52fSWarner Losh }
238*1b99d52fSWarner Losh 
239*1b99d52fSWarner Losh static void
240*1b99d52fSWarner Losh mmcnull_intr_sdio_newintr(void *xsc) {
241*1b99d52fSWarner Losh 	struct mmcnull_softc *sc;
242*1b99d52fSWarner Losh 	struct cam_path *dpath;
243*1b99d52fSWarner Losh 
244*1b99d52fSWarner Losh 	sc = (struct mmcnull_softc *) xsc;
245*1b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
246*1b99d52fSWarner Losh 	device_printf(sc->dev, "mmcnull_intr_sdio_newintr()\n");
247*1b99d52fSWarner Losh 
248*1b99d52fSWarner Losh 	/* Our path */
249*1b99d52fSWarner Losh 	if (xpt_create_path(&dpath, NULL, cam_sim_path(sc->sim), 0, 0) != CAM_REQ_CMP) {
250*1b99d52fSWarner Losh 		device_printf(sc->dev, "mmcnull_intr_sdio_newintr(): cannot create path\n");
251*1b99d52fSWarner Losh 		return;
252*1b99d52fSWarner Losh 	}
253*1b99d52fSWarner Losh 	xpt_async(AC_UNIT_ATTENTION, dpath, NULL);
254*1b99d52fSWarner Losh 	xpt_free_path(dpath);
255*1b99d52fSWarner Losh }
256*1b99d52fSWarner Losh 
257*1b99d52fSWarner Losh static void
258*1b99d52fSWarner Losh mmcnull_intr_sdio(void *xsc) {
259*1b99d52fSWarner Losh 	struct mmcnull_softc *sc;
260*1b99d52fSWarner Losh 	union ccb *ccb;
261*1b99d52fSWarner Losh 	struct ccb_mmcio *mmcio;
262*1b99d52fSWarner Losh 
263*1b99d52fSWarner Losh 	sc = (struct mmcnull_softc *) xsc;
264*1b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
265*1b99d52fSWarner Losh 
266*1b99d52fSWarner Losh 	ccb = sc->cur_ccb;
267*1b99d52fSWarner Losh 	mmcio = &ccb->mmcio;
268*1b99d52fSWarner Losh 	device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n",
269*1b99d52fSWarner Losh 		      mmcio->cmd.opcode);
270*1b99d52fSWarner Losh 
271*1b99d52fSWarner Losh 	switch (mmcio->cmd.opcode) {
272*1b99d52fSWarner Losh 	case MMC_GO_IDLE_STATE:
273*1b99d52fSWarner Losh 		device_printf(sc->dev, "Reset device\n");
274*1b99d52fSWarner Losh 		break;
275*1b99d52fSWarner Losh 	case SD_SEND_IF_COND:
276*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1AA; // To match mmc_xpt expectations :-)
277*1b99d52fSWarner Losh 		break;
278*1b99d52fSWarner Losh 	case MMC_APP_CMD:
279*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = R1_APP_CMD;
280*1b99d52fSWarner Losh 		break;
281*1b99d52fSWarner Losh 	case IO_SEND_OP_COND:
282*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x12345678;
283*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] |= ~ R4_IO_MEM_PRESENT;
284*1b99d52fSWarner Losh 		break;
285*1b99d52fSWarner Losh 	case SD_SEND_RELATIVE_ADDR:
286*1b99d52fSWarner Losh 	case MMC_SELECT_CARD:
287*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1 << 16;
288*1b99d52fSWarner Losh 		break;
289*1b99d52fSWarner Losh 	case ACMD_SD_SEND_OP_COND:
290*1b99d52fSWarner Losh 		/* TODO: steal valid OCR from somewhere :-) */
291*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x123;
292*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] |= MMC_OCR_CARD_BUSY;
293*1b99d52fSWarner Losh 		break;
294*1b99d52fSWarner Losh 	case MMC_ALL_SEND_CID:
295*1b99d52fSWarner Losh 		mmcio->cmd.resp[0] = 0x1234;
296*1b99d52fSWarner Losh 		mmcio->cmd.resp[1] = 0x5678;
297*1b99d52fSWarner Losh 		mmcio->cmd.resp[2] = 0x9ABC;
298*1b99d52fSWarner Losh 		mmcio->cmd.resp[3] = 0xDEF0;
299*1b99d52fSWarner Losh 		break;
300*1b99d52fSWarner Losh 	case MMC_READ_SINGLE_BLOCK:
301*1b99d52fSWarner Losh 	case MMC_READ_MULTIPLE_BLOCK:
302*1b99d52fSWarner Losh 		strcpy(mmcio->cmd.data->data, "WTF?!");
303*1b99d52fSWarner Losh 		break;
304*1b99d52fSWarner Losh 	case SD_IO_RW_DIRECT:
305*1b99d52fSWarner Losh 		device_printf(sc->dev, "Scheduling interrupt generation...\n");
306*1b99d52fSWarner Losh 		callout_reset(&sc->tick, hz / 10, mmcnull_intr_sdio_newintr, sc);
307*1b99d52fSWarner Losh 		break;
308*1b99d52fSWarner Losh 	default:
309*1b99d52fSWarner Losh 		device_printf(sc->dev, "mmcnull_intr_sdio: unknown command\n");
310*1b99d52fSWarner Losh 	}
311*1b99d52fSWarner Losh 	ccb->ccb_h.status = CAM_REQ_CMP;
312*1b99d52fSWarner Losh 
313*1b99d52fSWarner Losh 	sc->cur_ccb = NULL;
314*1b99d52fSWarner Losh 	xpt_done(ccb);
315*1b99d52fSWarner Losh }
316*1b99d52fSWarner Losh 
317*1b99d52fSWarner Losh /*
318*1b99d52fSWarner Losh  * This is a MMC IO handler
319*1b99d52fSWarner Losh  * It extracts MMC command from CCB and sends it
320*1b99d52fSWarner Losh  * to the h/w
321*1b99d52fSWarner Losh  */
322*1b99d52fSWarner Losh static void
323*1b99d52fSWarner Losh mmcnull_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
324*1b99d52fSWarner Losh {
325*1b99d52fSWarner Losh 	struct mmcnull_softc *sc;
326*1b99d52fSWarner Losh 	struct ccb_mmcio *mmcio;
327*1b99d52fSWarner Losh 
328*1b99d52fSWarner Losh 	sc = cam_sim_softc(sim);
329*1b99d52fSWarner Losh 	mmcio = &ccb->mmcio;
330*1b99d52fSWarner Losh 	ccb->ccb_h.status = CAM_REQ_INPROG;
331*1b99d52fSWarner Losh 	sc->cur_ccb = ccb;
332*1b99d52fSWarner Losh 
333*1b99d52fSWarner Losh 	/* Real h/w will wait for the interrupt */
334*1b99d52fSWarner Losh 	if (is_sdio_mode)
335*1b99d52fSWarner Losh 		callout_reset(&sc->tick, hz / 10, mmcnull_intr_sdio, sc);
336*1b99d52fSWarner Losh 	else
337*1b99d52fSWarner Losh 		callout_reset(&sc->tick, hz / 10, mmcnull_intr_sd, sc);
338*1b99d52fSWarner Losh }
339*1b99d52fSWarner Losh 
340*1b99d52fSWarner Losh static void
341*1b99d52fSWarner Losh mmcnull_action_sd(struct cam_sim *sim, union ccb *ccb)
342*1b99d52fSWarner Losh {
343*1b99d52fSWarner Losh 	struct mmcnull_softc *sc;
344*1b99d52fSWarner Losh 
345*1b99d52fSWarner Losh 	sc = cam_sim_softc(sim);
346*1b99d52fSWarner Losh 	if (sc == NULL) {
347*1b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
348*1b99d52fSWarner Losh 		xpt_done(ccb);
349*1b99d52fSWarner Losh 		return;
350*1b99d52fSWarner Losh 	}
351*1b99d52fSWarner Losh 
352*1b99d52fSWarner Losh 	mtx_assert(&sc->sc_mtx, MA_OWNED);
353*1b99d52fSWarner Losh 
354*1b99d52fSWarner Losh 	device_printf(sc->dev, "action: func_code %0x\n", ccb->ccb_h.func_code);
355*1b99d52fSWarner Losh 
356*1b99d52fSWarner Losh 	switch (ccb->ccb_h.func_code) {
357*1b99d52fSWarner Losh 	case XPT_PATH_INQ:
358*1b99d52fSWarner Losh 	{
359*1b99d52fSWarner Losh 		struct ccb_pathinq *cpi;
360*1b99d52fSWarner Losh 
361*1b99d52fSWarner Losh 		cpi = &ccb->cpi;
362*1b99d52fSWarner Losh 		cpi->version_num = 1;
363*1b99d52fSWarner Losh 		cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_16;
364*1b99d52fSWarner Losh 		cpi->target_sprt = 0;
365*1b99d52fSWarner Losh 		cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
366*1b99d52fSWarner Losh 		cpi->hba_eng_cnt = 0;
367*1b99d52fSWarner Losh 		cpi->max_target = 0;
368*1b99d52fSWarner Losh 		cpi->max_lun = 0;
369*1b99d52fSWarner Losh 		cpi->initiator_id = 1;
370*1b99d52fSWarner Losh 		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
371*1b99d52fSWarner Losh 		strncpy(cpi->hba_vid, "FreeBSD Foundation", HBA_IDLEN);
372*1b99d52fSWarner Losh 		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
373*1b99d52fSWarner Losh 		cpi->unit_number = cam_sim_unit(sim);
374*1b99d52fSWarner Losh 		cpi->bus_id = cam_sim_bus(sim);
375*1b99d52fSWarner Losh 		cpi->base_transfer_speed = 100; /* XXX WTF? */
376*1b99d52fSWarner Losh 		cpi->protocol = PROTO_MMCSD;
377*1b99d52fSWarner Losh 		cpi->protocol_version = SCSI_REV_0;
378*1b99d52fSWarner Losh 		cpi->transport = XPORT_MMCSD;
379*1b99d52fSWarner Losh 		cpi->transport_version = 0;
380*1b99d52fSWarner Losh 
381*1b99d52fSWarner Losh 		cpi->ccb_h.status = CAM_REQ_CMP;
382*1b99d52fSWarner Losh 		break;
383*1b99d52fSWarner Losh 	}
384*1b99d52fSWarner Losh 	case XPT_GET_TRAN_SETTINGS:
385*1b99d52fSWarner Losh 	{
386*1b99d52fSWarner Losh 		struct ccb_trans_settings *cts = &ccb->cts;
387*1b99d52fSWarner Losh 		struct ccb_trans_settings_mmc *mcts;
388*1b99d52fSWarner Losh 		mcts = &ccb->cts.proto_specific.mmc;
389*1b99d52fSWarner Losh 
390*1b99d52fSWarner Losh                 device_printf(sc->dev, "Got XPT_GET_TRAN_SETTINGS\n");
391*1b99d52fSWarner Losh 
392*1b99d52fSWarner Losh                 cts->protocol = PROTO_MMCSD;
393*1b99d52fSWarner Losh                 cts->protocol_version = 0;
394*1b99d52fSWarner Losh                 cts->transport = XPORT_MMCSD;
395*1b99d52fSWarner Losh                 cts->transport_version = 0;
396*1b99d52fSWarner Losh                 cts->xport_specific.valid = 0;
397*1b99d52fSWarner Losh 		mcts->host_f_max = 12000000;
398*1b99d52fSWarner Losh 		mcts->host_f_min = 200000;
399*1b99d52fSWarner Losh 		mcts->host_ocr = 1; /* Fix this */
400*1b99d52fSWarner Losh                 ccb->ccb_h.status = CAM_REQ_CMP;
401*1b99d52fSWarner Losh                 break;
402*1b99d52fSWarner Losh         }
403*1b99d52fSWarner Losh 	case XPT_SET_TRAN_SETTINGS:
404*1b99d52fSWarner Losh 		device_printf(sc->dev, "Got XPT_SET_TRAN_SETTINGS, should update IOS...\n");
405*1b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_CMP;
406*1b99d52fSWarner Losh 		break;
407*1b99d52fSWarner Losh 	case XPT_RESET_BUS:
408*1b99d52fSWarner Losh 		device_printf(sc->dev, "Got XPT_RESET_BUS, ACK it...\n");
409*1b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_CMP;
410*1b99d52fSWarner Losh                 break;
411*1b99d52fSWarner Losh 	case XPT_MMC_IO:
412*1b99d52fSWarner Losh 		/*
413*1b99d52fSWarner Losh                  * Here is the HW-dependent part of
414*1b99d52fSWarner Losh 		 * sending the command to the underlying h/w
415*1b99d52fSWarner Losh 		 * At some point in the future an interrupt comes.
416*1b99d52fSWarner Losh 		 * Then the request will be marked as completed.
417*1b99d52fSWarner Losh 		 */
418*1b99d52fSWarner Losh 		device_printf(sc->dev, "Got XPT_MMC_IO\n");
419*1b99d52fSWarner Losh 		mmcnull_handle_mmcio(sim, ccb);
420*1b99d52fSWarner Losh 		return;
421*1b99d52fSWarner Losh 		break;
422*1b99d52fSWarner Losh         case XPT_RESET_DEV:
423*1b99d52fSWarner Losh                 /* This is sent by `camcontrol reset`*/
424*1b99d52fSWarner Losh                 device_printf(sc->dev, "Got XPT_RESET_DEV\n");
425*1b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_CMP;
426*1b99d52fSWarner Losh                 break;
427*1b99d52fSWarner Losh 	default:
428*1b99d52fSWarner Losh 		device_printf(sc->dev, "Func code %d is unknown\n", ccb->ccb_h.func_code);
429*1b99d52fSWarner Losh 		ccb->ccb_h.status = CAM_REQ_INVALID;
430*1b99d52fSWarner Losh 		break;
431*1b99d52fSWarner Losh 	}
432*1b99d52fSWarner Losh 	xpt_done(ccb);
433*1b99d52fSWarner Losh 	return;
434*1b99d52fSWarner Losh }
435*1b99d52fSWarner Losh 
436*1b99d52fSWarner Losh static void
437*1b99d52fSWarner Losh mmcnull_action_sdio(struct cam_sim *sim, union ccb *ccb) {
438*1b99d52fSWarner Losh 	mmcnull_action_sd(sim, ccb);
439*1b99d52fSWarner Losh }
440*1b99d52fSWarner Losh 
441*1b99d52fSWarner Losh static void
442*1b99d52fSWarner Losh mmcnull_poll(struct cam_sim *sim)
443*1b99d52fSWarner Losh {
444*1b99d52fSWarner Losh 	return;
445*1b99d52fSWarner Losh }
446*1b99d52fSWarner Losh 
447*1b99d52fSWarner Losh 
448*1b99d52fSWarner Losh static device_method_t mmcnull_methods[] = {
449*1b99d52fSWarner Losh 	/* Device interface */
450*1b99d52fSWarner Losh 	DEVMETHOD(device_identify,      mmcnull_identify),
451*1b99d52fSWarner Losh 	DEVMETHOD(device_probe,         mmcnull_probe),
452*1b99d52fSWarner Losh 	DEVMETHOD(device_attach,        mmcnull_attach),
453*1b99d52fSWarner Losh 	DEVMETHOD(device_detach,        mmcnull_detach),
454*1b99d52fSWarner Losh 	DEVMETHOD_END
455*1b99d52fSWarner Losh };
456*1b99d52fSWarner Losh 
457*1b99d52fSWarner Losh static driver_t mmcnull_driver = {
458*1b99d52fSWarner Losh 	"mmcnull", mmcnull_methods, sizeof(struct mmcnull_softc)
459*1b99d52fSWarner Losh };
460*1b99d52fSWarner Losh 
461*1b99d52fSWarner Losh static devclass_t mmcnull_devclass;
462*1b99d52fSWarner Losh 
463*1b99d52fSWarner Losh DRIVER_MODULE(mmcnull, isa, mmcnull_driver, mmcnull_devclass, 0, 0);
464