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