1/*-
2 * Copyright (c) 2020-2021 Emmanuel Vadot <manu@FreeBSD.org>
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,
18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/param.h>
27#include <sys/kernel.h>
28#include <sys/systm.h>
29#include <sys/malloc.h>
30#include <sys/mutex.h>
31
32#include <cam/cam.h>
33#include <cam/cam_ccb.h>
34#include <cam/cam_debug.h>
35#include <cam/cam_sim.h>
36#include <cam/cam_xpt_sim.h>
37#include <cam/mmc/mmc_sim.h>
38
39#include "mmc_sim_if.h"
40
41static void
42mmc_cam_default_poll(struct cam_sim *sim)
43{
44	struct mmc_sim *mmc_sim;
45
46	mmc_sim = cam_sim_softc(sim);
47	MMC_SIM_CAM_POLL(mmc_sim->dev);
48}
49
50static void
51mmc_sim_task(void *arg, int pending)
52{
53	struct mmc_sim *mmc_sim;
54	struct ccb_trans_settings *cts;
55	int rv;
56
57	mmc_sim = arg;
58
59	if (mmc_sim->ccb == NULL)
60		return;
61
62	cts = &mmc_sim->ccb->cts;
63	switch (mmc_sim->ccb->ccb_h.func_code) {
64	case XPT_MMC_GET_TRAN_SETTINGS:
65		rv = MMC_SIM_GET_TRAN_SETTINGS(mmc_sim->dev, &cts->proto_specific.mmc);
66		if (rv != 0)
67			mmc_sim->ccb->ccb_h.status = CAM_REQ_INVALID;
68		else
69			mmc_sim->ccb->ccb_h.status = CAM_REQ_CMP;
70		break;
71	case XPT_MMC_SET_TRAN_SETTINGS:
72		rv = MMC_SIM_SET_TRAN_SETTINGS(mmc_sim->dev, &cts->proto_specific.mmc);
73		if (rv != 0)
74			mmc_sim->ccb->ccb_h.status = CAM_REQ_INVALID;
75		else
76			mmc_sim->ccb->ccb_h.status = CAM_REQ_CMP;
77		break;
78	default:
79		panic("Unsupported ccb func %x\n", mmc_sim->ccb->ccb_h.func_code);
80		break;
81	}
82
83	xpt_done(mmc_sim->ccb);
84	mmc_sim->ccb = NULL;
85}
86
87
88static void
89mmc_cam_sim_default_action(struct cam_sim *sim, union ccb *ccb)
90{
91	struct mmc_sim *mmc_sim;
92	struct ccb_trans_settings_mmc mmc;
93	int rv;
94
95	mmc_sim = cam_sim_softc(sim);
96
97	mtx_assert(&mmc_sim->mtx, MA_OWNED);
98
99	if (mmc_sim->ccb != NULL) {
100		ccb->ccb_h.status = CAM_BUSY;
101		xpt_done(ccb);
102		return;
103	}
104
105	switch (ccb->ccb_h.func_code) {
106	case XPT_PATH_INQ:
107		rv = MMC_SIM_GET_TRAN_SETTINGS(mmc_sim->dev, &mmc);
108		if (rv != 0) {
109			ccb->ccb_h.status = CAM_REQ_INVALID;
110		} else {
111			mmc_path_inq(&ccb->cpi, "Deglitch Networks",
112			    sim, mmc.host_max_data);
113		}
114		break;
115	case XPT_GET_TRAN_SETTINGS:
116	{
117		struct ccb_trans_settings *cts = &ccb->cts;
118
119		rv = MMC_SIM_GET_TRAN_SETTINGS(mmc_sim->dev, &cts->proto_specific.mmc);
120		if (rv != 0)
121			ccb->ccb_h.status = CAM_REQ_INVALID;
122		else {
123			cts->protocol = PROTO_MMCSD;
124			cts->protocol_version = 1;
125			cts->transport = XPORT_MMCSD;
126			cts->transport_version = 1;
127			cts->xport_specific.valid = 0;
128			ccb->ccb_h.status = CAM_REQ_CMP;
129		}
130		break;
131	}
132	case XPT_MMC_GET_TRAN_SETTINGS:
133	{
134		ccb->ccb_h.status = CAM_SIM_QUEUED;
135		mmc_sim->ccb = ccb;
136		taskqueue_enqueue(taskqueue_thread, &mmc_sim->sim_task);
137		return;
138		/* NOTREACHED */
139		break;
140	}
141	case XPT_SET_TRAN_SETTINGS:
142	{
143		struct ccb_trans_settings *cts = &ccb->cts;
144
145		rv = MMC_SIM_SET_TRAN_SETTINGS(mmc_sim->dev, &cts->proto_specific.mmc);
146		if (rv != 0)
147			ccb->ccb_h.status = CAM_REQ_INVALID;
148		else
149			ccb->ccb_h.status = CAM_REQ_CMP;
150		break;
151	}
152	case XPT_MMC_SET_TRAN_SETTINGS:
153	{
154		ccb->ccb_h.status = CAM_SIM_QUEUED;
155		mmc_sim->ccb = ccb;
156		taskqueue_enqueue(taskqueue_thread, &mmc_sim->sim_task);
157		return;
158		/* NOTREACHED */
159		break;
160	}
161	case XPT_RESET_BUS:
162		ccb->ccb_h.status = CAM_REQ_CMP;
163		break;
164	case XPT_MMC_IO:
165	{
166		rv = MMC_SIM_CAM_REQUEST(mmc_sim->dev, ccb);
167		if (rv != 0)
168			ccb->ccb_h.status = CAM_SIM_QUEUED;
169		return;
170		/* NOTREACHED */
171		break;
172	}
173	default:
174		ccb->ccb_h.status = CAM_REQ_INVALID;
175		break;
176	}
177	xpt_done(ccb);
178	return;
179}
180
181int
182mmc_cam_sim_alloc(device_t dev, const char *name, struct mmc_sim *mmc_sim)
183{
184	kobjop_desc_t kobj_desc;
185	kobj_method_t *kobj_method;
186
187	mmc_sim->dev = dev;
188
189	if ((mmc_sim->devq = cam_simq_alloc(1)) == NULL) {
190		goto fail;
191	}
192
193	snprintf(mmc_sim->name, sizeof(mmc_sim->name), "%s_sim", name);
194	mtx_init(&mmc_sim->mtx, mmc_sim->name, NULL, MTX_DEF);
195
196	/* Provide sim_poll hook only if the device has the poll method. */
197	kobj_desc = &mmc_sim_cam_poll_desc;
198	kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
199	    kobj_desc);
200	mmc_sim->sim = cam_sim_alloc(mmc_cam_sim_default_action,
201	    kobj_method == &kobj_desc->deflt ? NULL : mmc_cam_default_poll,
202	    mmc_sim->name, mmc_sim, device_get_unit(dev),
203	    &mmc_sim->mtx, 1, 1, mmc_sim->devq);
204
205	if (mmc_sim->sim == NULL) {
206		cam_simq_free(mmc_sim->devq);
207		device_printf(dev, "cannot allocate CAM SIM\n");
208		goto fail;
209	}
210
211	mtx_lock(&mmc_sim->mtx);
212	if (xpt_bus_register(mmc_sim->sim, dev, 0) != 0) {
213		device_printf(dev, "cannot register SCSI pass-through bus\n");
214		cam_sim_free(mmc_sim->sim, FALSE);
215		cam_simq_free(mmc_sim->devq);
216		mtx_unlock(&mmc_sim->mtx);
217		goto fail;
218	}
219
220	mtx_unlock(&mmc_sim->mtx);
221	TASK_INIT(&mmc_sim->sim_task, 0, mmc_sim_task, mmc_sim);
222
223	return (0);
224
225fail:
226	mmc_cam_sim_free(mmc_sim);
227	return (1);
228}
229
230void
231mmc_cam_sim_free(struct mmc_sim *mmc_sim)
232{
233
234	if (mmc_sim->sim != NULL) {
235		mtx_lock(&mmc_sim->mtx);
236		xpt_bus_deregister(cam_sim_path(mmc_sim->sim));
237		cam_sim_free(mmc_sim->sim, FALSE);
238		mtx_unlock(&mmc_sim->mtx);
239	}
240
241	if (mmc_sim->devq != NULL)
242		cam_simq_free(mmc_sim->devq);
243}
244
245void
246mmc_cam_sim_discover(struct mmc_sim *mmc_sim)
247{
248
249	mmccam_start_discovery(mmc_sim->sim);
250}
251