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