xref: /openbsd-src/sys/dev/sdmmc/sdmmc_scsi.c (revision 10d2c6b32ad1ecf5e27987d46d46acbd2994a396)
1*10d2c6b3Sdlg /*	$OpenBSD: sdmmc_scsi.c,v 1.63 2023/04/19 01:46:10 dlg Exp $	*/
2aae4fe77Suwe 
3aae4fe77Suwe /*
4aae4fe77Suwe  * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
5aae4fe77Suwe  *
6aae4fe77Suwe  * Permission to use, copy, modify, and distribute this software for any
7aae4fe77Suwe  * purpose with or without fee is hereby granted, provided that the above
8aae4fe77Suwe  * copyright notice and this permission notice appear in all copies.
9aae4fe77Suwe  *
10aae4fe77Suwe  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11aae4fe77Suwe  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12aae4fe77Suwe  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13aae4fe77Suwe  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14aae4fe77Suwe  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15aae4fe77Suwe  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16aae4fe77Suwe  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17aae4fe77Suwe  */
18aae4fe77Suwe 
19a6fd99a7Suwe /* A SCSI adapter emulation to access SD/MMC memory cards */
20aae4fe77Suwe 
21aae4fe77Suwe #include <sys/param.h>
22aae4fe77Suwe #include <sys/buf.h>
2396813442Skettenis #include <sys/device.h>
24aae4fe77Suwe #include <sys/malloc.h>
25624448b6Sjsg #include <sys/proc.h>
26aae4fe77Suwe #include <sys/systm.h>
27aae4fe77Suwe 
28aae4fe77Suwe #include <scsi/scsi_all.h>
29aae4fe77Suwe #include <scsi/scsi_disk.h>
30aae4fe77Suwe #include <scsi/scsiconf.h>
31aae4fe77Suwe 
32aae4fe77Suwe #include <dev/sdmmc/sdmmc_scsi.h>
33aae4fe77Suwe #include <dev/sdmmc/sdmmcvar.h>
34aae4fe77Suwe 
35f3036462Sjmatthew #ifdef HIBERNATE
36f3036462Sjmatthew #include <sys/hibernate.h>
37f3036462Sjmatthew #include <sys/disk.h>
38f3036462Sjmatthew #include <sys/disklabel.h>
39f3036462Sjmatthew #include <sys/rwlock.h>
40f3036462Sjmatthew #endif
41f3036462Sjmatthew 
423ba30b1bSmatthew #define SDMMC_SCSIID_HOST	0x00
433ba30b1bSmatthew #define SDMMC_SCSIID_MAX	0x0f
44aae4fe77Suwe 
45a6fd99a7Suwe #define SDMMC_SCSI_MAXCMDS	8
46a6fd99a7Suwe 
47a6fd99a7Suwe struct sdmmc_scsi_target {
48a6fd99a7Suwe 	struct sdmmc_function *card;
49a6fd99a7Suwe };
50a6fd99a7Suwe 
51a6fd99a7Suwe struct sdmmc_ccb {
52a6fd99a7Suwe 	struct sdmmc_scsi_softc *ccb_scbus;
53a6fd99a7Suwe 	struct scsi_xfer *ccb_xs;
54a6fd99a7Suwe 	int ccb_flags;
55a6fd99a7Suwe #define SDMMC_CCB_F_ERR		0x0001
56a6fd99a7Suwe 	u_int32_t ccb_blockno;
57a6fd99a7Suwe 	u_int32_t ccb_blockcnt;
58a6fd99a7Suwe 	volatile enum {
59a6fd99a7Suwe 		SDMMC_CCB_FREE,
60a6fd99a7Suwe 		SDMMC_CCB_READY,
61a6fd99a7Suwe 		SDMMC_CCB_QUEUED
62a6fd99a7Suwe 	} ccb_state;
63a6fd99a7Suwe 	struct sdmmc_command ccb_cmd;
64a6fd99a7Suwe 	struct sdmmc_task ccb_task;
65a6fd99a7Suwe 	TAILQ_ENTRY(sdmmc_ccb) ccb_link;
66a6fd99a7Suwe };
67a6fd99a7Suwe 
68a6fd99a7Suwe TAILQ_HEAD(sdmmc_ccb_list, sdmmc_ccb);
69a6fd99a7Suwe 
70a6fd99a7Suwe struct sdmmc_scsi_softc {
71a6fd99a7Suwe 	struct device *sc_child;
72a6fd99a7Suwe 	struct sdmmc_scsi_target *sc_tgt;
73a6fd99a7Suwe 	int sc_ntargets;
74a6fd99a7Suwe 	struct sdmmc_ccb *sc_ccbs;		/* allocated ccbs */
75870d2ca8Sderaadt 	int		sc_nccbs;
76a6fd99a7Suwe 	struct sdmmc_ccb_list sc_ccb_freeq;	/* free ccbs */
77a6fd99a7Suwe 	struct sdmmc_ccb_list sc_ccb_runq;	/* queued ccbs */
78abf17191Skrw 	struct mutex sc_ccb_mtx;
79abf17191Skrw 	struct scsi_iopool sc_iopool;
80a6fd99a7Suwe };
81a6fd99a7Suwe 
82a6fd99a7Suwe int	sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *, int);
83a6fd99a7Suwe void	sdmmc_free_ccbs(struct sdmmc_scsi_softc *);
84abf17191Skrw void	*sdmmc_ccb_alloc(void *);
85abf17191Skrw void	sdmmc_ccb_free(void *, void *);
86a6fd99a7Suwe 
87bae03be6Skrw void	sdmmc_scsi_cmd(struct scsi_xfer *);
881b59d03aSdlg void	sdmmc_inquiry(struct scsi_xfer *);
89bae03be6Skrw void	sdmmc_start_xs(struct sdmmc_softc *, struct sdmmc_ccb *);
90a6fd99a7Suwe void	sdmmc_complete_xs(void *);
91a6fd99a7Suwe void	sdmmc_done_xs(struct sdmmc_ccb *);
92a6fd99a7Suwe void	sdmmc_stimeout(void *);
939f36bc94Skrw void	sdmmc_minphys(struct buf *, struct scsi_link *);
94aae4fe77Suwe 
95a454aff3Snaddy const struct scsi_adapter sdmmc_switch = {
964d7d9900Skrw 	sdmmc_scsi_cmd, sdmmc_minphys, NULL, NULL, NULL
974d7d9900Skrw };
984d7d9900Skrw 
99aae4fe77Suwe #ifdef SDMMC_DEBUG
100aae4fe77Suwe #define DPRINTF(s)	printf s
101aae4fe77Suwe #else
102aae4fe77Suwe #define DPRINTF(s)	/**/
103aae4fe77Suwe #endif
104aae4fe77Suwe 
105aae4fe77Suwe void
sdmmc_scsi_attach(struct sdmmc_softc * sc)106aae4fe77Suwe sdmmc_scsi_attach(struct sdmmc_softc *sc)
107aae4fe77Suwe {
1086fed03a2Sgrange 	struct sdmmc_attach_args saa;
109aae4fe77Suwe 	struct sdmmc_scsi_softc *scbus;
110cfd1c195Suwe 	struct sdmmc_function *sf;
111aae4fe77Suwe 
1129de6240fSblambert 	rw_assert_wrlock(&sc->sc_lock);
1134f39d9f1Sblambert 
1146c863e75Skettenis 	scbus = malloc(sizeof *scbus, M_DEVBUF, M_WAITOK | M_ZERO);
115aae4fe77Suwe 
116870d2ca8Sderaadt 	scbus->sc_tgt = mallocarray(sizeof(*scbus->sc_tgt),
1173ba30b1bSmatthew 	    (SDMMC_SCSIID_MAX+1), M_DEVBUF, M_WAITOK | M_ZERO);
118aae4fe77Suwe 
119aae4fe77Suwe 	/*
120aae4fe77Suwe 	 * Each card that sent us a CID in the identification stage
1213ba30b1bSmatthew 	 * gets a SCSI ID > 0, whether it is a memory card or not.
122aae4fe77Suwe 	 */
1233ba30b1bSmatthew 	scbus->sc_ntargets = 1;
124cfd1c195Suwe 	SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
1253ba30b1bSmatthew 		if (scbus->sc_ntargets >= SDMMC_SCSIID_MAX+1)
126aae4fe77Suwe 			break;
127cfd1c195Suwe 		scbus->sc_tgt[scbus->sc_ntargets].card = sf;
128aae4fe77Suwe 		scbus->sc_ntargets++;
129aae4fe77Suwe 	}
130aae4fe77Suwe 
131a6fd99a7Suwe 	/* Preallocate some CCBs and initialize the CCB lists. */
132a6fd99a7Suwe 	if (sdmmc_alloc_ccbs(scbus, SDMMC_SCSI_MAXCMDS) != 0) {
133a6fd99a7Suwe 		printf("%s: can't allocate ccbs\n", sc->sc_dev.dv_xname);
134a6fd99a7Suwe 		goto free_sctgt;
135a6fd99a7Suwe 	}
136a6fd99a7Suwe 
137aae4fe77Suwe 	sc->sc_scsibus = scbus;
138aae4fe77Suwe 
1399b4e521aSkrw 	saa.sf = NULL;
140ead808c4Skrw 	saa.saa.saa_adapter_target = SDMMC_SCSIID_HOST;
141ead808c4Skrw 	saa.saa.saa_adapter_buswidth = scbus->sc_ntargets;
142ead808c4Skrw 	saa.saa.saa_adapter_softc = sc;
143ead808c4Skrw 	saa.saa.saa_luns = 1;
144ead808c4Skrw 	saa.saa.saa_adapter = &sdmmc_switch;
145e5eae15dSkrw 	saa.saa.saa_openings = 1;
146e5eae15dSkrw 	saa.saa.saa_pool = &scbus->sc_iopool;
147e5eae15dSkrw 	saa.saa.saa_quirks = saa.saa.saa_flags = 0;
148e5eae15dSkrw 	saa.saa.saa_wwpn = saa.saa.saa_wwnn = 0;
14973d09fc5Sdlg 
1503ba30b1bSmatthew 	scbus->sc_child = config_found(&sc->sc_dev, &saa, scsiprint);
151a6fd99a7Suwe 	if (scbus->sc_child == NULL) {
152a6fd99a7Suwe 		printf("%s: can't attach scsibus\n", sc->sc_dev.dv_xname);
153a6fd99a7Suwe 		goto free_ccbs;
154a6fd99a7Suwe 	}
155a6fd99a7Suwe 	return;
156a6fd99a7Suwe 
157a6fd99a7Suwe  free_ccbs:
158a6fd99a7Suwe 	sc->sc_scsibus = NULL;
159a6fd99a7Suwe 	sdmmc_free_ccbs(scbus);
160a6fd99a7Suwe  free_sctgt:
161870d2ca8Sderaadt 	free(scbus->sc_tgt, M_DEVBUF,
162870d2ca8Sderaadt 	    sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1));
16387985770Sderaadt 	free(scbus, M_DEVBUF, sizeof *scbus);
164aae4fe77Suwe }
165aae4fe77Suwe 
166aae4fe77Suwe void
sdmmc_scsi_detach(struct sdmmc_softc * sc)167aae4fe77Suwe sdmmc_scsi_detach(struct sdmmc_softc *sc)
168aae4fe77Suwe {
169aae4fe77Suwe 	struct sdmmc_scsi_softc *scbus;
170a6fd99a7Suwe 	struct sdmmc_ccb *ccb;
171a6fd99a7Suwe 	int s;
172aae4fe77Suwe 
1739de6240fSblambert 	rw_assert_wrlock(&sc->sc_lock);
1744f39d9f1Sblambert 
175aae4fe77Suwe 	scbus = sc->sc_scsibus;
176a6fd99a7Suwe 	if (scbus == NULL)
177a6fd99a7Suwe 		return;
178a6fd99a7Suwe 
179a6fd99a7Suwe 	/* Complete all open scsi xfers. */
180a6fd99a7Suwe 	s = splbio();
181a6fd99a7Suwe 	for (ccb = TAILQ_FIRST(&scbus->sc_ccb_runq); ccb != NULL;
182a6fd99a7Suwe 	     ccb = TAILQ_FIRST(&scbus->sc_ccb_runq))
183a6fd99a7Suwe 		sdmmc_stimeout(ccb);
184a6fd99a7Suwe 	splx(s);
185aae4fe77Suwe 
186aae4fe77Suwe 	if (scbus->sc_child != NULL)
187aae4fe77Suwe 		config_detach(scbus->sc_child, DETACH_FORCE);
188aae4fe77Suwe 
189aae4fe77Suwe 	if (scbus->sc_tgt != NULL)
190870d2ca8Sderaadt 		free(scbus->sc_tgt, M_DEVBUF,
191870d2ca8Sderaadt 		    sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1));
192aae4fe77Suwe 
193a6fd99a7Suwe 	sdmmc_free_ccbs(scbus);
19487985770Sderaadt 	free(scbus, M_DEVBUF, sizeof *scbus);
195aae4fe77Suwe 	sc->sc_scsibus = NULL;
196aae4fe77Suwe }
197aae4fe77Suwe 
198a6fd99a7Suwe /*
199a6fd99a7Suwe  * CCB management
200a6fd99a7Suwe  */
201a6fd99a7Suwe 
202a6fd99a7Suwe int
sdmmc_alloc_ccbs(struct sdmmc_scsi_softc * scbus,int nccbs)203a6fd99a7Suwe sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *scbus, int nccbs)
204a6fd99a7Suwe {
205a6fd99a7Suwe 	struct sdmmc_ccb *ccb;
206a6fd99a7Suwe 	int i;
207a6fd99a7Suwe 
2080f0d0f95Sdoug 	scbus->sc_ccbs = mallocarray(nccbs, sizeof(struct sdmmc_ccb),
209a6fd99a7Suwe 	    M_DEVBUF, M_NOWAIT);
210a6fd99a7Suwe 	if (scbus->sc_ccbs == NULL)
211a6fd99a7Suwe 		return 1;
212870d2ca8Sderaadt 	scbus->sc_nccbs = nccbs;
213a6fd99a7Suwe 
214a6fd99a7Suwe 	TAILQ_INIT(&scbus->sc_ccb_freeq);
215a6fd99a7Suwe 	TAILQ_INIT(&scbus->sc_ccb_runq);
216abf17191Skrw 	mtx_init(&scbus->sc_ccb_mtx, IPL_BIO);
217abf17191Skrw 	scsi_iopool_init(&scbus->sc_iopool, scbus, sdmmc_ccb_alloc,
218abf17191Skrw 	    sdmmc_ccb_free);
219a6fd99a7Suwe 
220a6fd99a7Suwe 	for (i = 0; i < nccbs; i++) {
221a6fd99a7Suwe 		ccb = &scbus->sc_ccbs[i];
222a6fd99a7Suwe 		ccb->ccb_scbus = scbus;
223a6fd99a7Suwe 		ccb->ccb_state = SDMMC_CCB_FREE;
224a6fd99a7Suwe 		ccb->ccb_flags = 0;
225a6fd99a7Suwe 		ccb->ccb_xs = NULL;
226a6fd99a7Suwe 
227a6fd99a7Suwe 		TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link);
228a6fd99a7Suwe 	}
229a6fd99a7Suwe 	return 0;
230a6fd99a7Suwe }
231a6fd99a7Suwe 
232a6fd99a7Suwe void
sdmmc_free_ccbs(struct sdmmc_scsi_softc * scbus)233a6fd99a7Suwe sdmmc_free_ccbs(struct sdmmc_scsi_softc *scbus)
234a6fd99a7Suwe {
235a6fd99a7Suwe 	if (scbus->sc_ccbs != NULL) {
236870d2ca8Sderaadt 		free(scbus->sc_ccbs, M_DEVBUF,
237870d2ca8Sderaadt 		    scbus->sc_nccbs * sizeof(struct sdmmc_ccb));
238a6fd99a7Suwe 		scbus->sc_ccbs = NULL;
239a6fd99a7Suwe 	}
240a6fd99a7Suwe }
241a6fd99a7Suwe 
242abf17191Skrw void *
sdmmc_ccb_alloc(void * xscbus)243abf17191Skrw sdmmc_ccb_alloc(void *xscbus)
244a6fd99a7Suwe {
245abf17191Skrw 	struct sdmmc_scsi_softc *scbus = xscbus;
246a6fd99a7Suwe 	struct sdmmc_ccb *ccb;
247a6fd99a7Suwe 
248abf17191Skrw 	mtx_enter(&scbus->sc_ccb_mtx);
249abf17191Skrw 	ccb = TAILQ_FIRST(&scbus->sc_ccb_freeq);
250a6fd99a7Suwe 	if (ccb != NULL) {
251a6fd99a7Suwe 		TAILQ_REMOVE(&scbus->sc_ccb_freeq, ccb, ccb_link);
252a6fd99a7Suwe 		ccb->ccb_state = SDMMC_CCB_READY;
253a6fd99a7Suwe 	}
254abf17191Skrw 	mtx_leave(&scbus->sc_ccb_mtx);
255abf17191Skrw 
256a6fd99a7Suwe 	return ccb;
257a6fd99a7Suwe }
258a6fd99a7Suwe 
259a6fd99a7Suwe void
sdmmc_ccb_free(void * xscbus,void * xccb)260abf17191Skrw sdmmc_ccb_free(void *xscbus, void *xccb)
261a6fd99a7Suwe {
262abf17191Skrw 	struct sdmmc_scsi_softc *scbus = xscbus;
263abf17191Skrw 	struct sdmmc_ccb *ccb = xccb;
264a6fd99a7Suwe 	int s;
265a6fd99a7Suwe 
266a6fd99a7Suwe 	s = splbio();
267a6fd99a7Suwe 	if (ccb->ccb_state == SDMMC_CCB_QUEUED)
268a6fd99a7Suwe 		TAILQ_REMOVE(&scbus->sc_ccb_runq, ccb, ccb_link);
269abf17191Skrw 	splx(s);
270abf17191Skrw 
271a6fd99a7Suwe 	ccb->ccb_state = SDMMC_CCB_FREE;
272a6fd99a7Suwe 	ccb->ccb_flags = 0;
273a6fd99a7Suwe 	ccb->ccb_xs = NULL;
274abf17191Skrw 
275abf17191Skrw 	mtx_enter(&scbus->sc_ccb_mtx);
276a6fd99a7Suwe 	TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link);
277abf17191Skrw 	mtx_leave(&scbus->sc_ccb_mtx);
278a6fd99a7Suwe }
279a6fd99a7Suwe 
280a6fd99a7Suwe /*
281a6fd99a7Suwe  * SCSI command emulation
282a6fd99a7Suwe  */
283a6fd99a7Suwe 
284a6fd99a7Suwe /* XXX move to some sort of "scsi emulation layer". */
285a6fd99a7Suwe static void
sdmmc_scsi_decode_rw(struct scsi_xfer * xs,u_int32_t * blocknop,u_int32_t * blockcntp)286a6fd99a7Suwe sdmmc_scsi_decode_rw(struct scsi_xfer *xs, u_int32_t *blocknop,
287a6fd99a7Suwe     u_int32_t *blockcntp)
288a6fd99a7Suwe {
289a6fd99a7Suwe 	struct scsi_rw *rw;
290eccd596dSkrw 	struct scsi_rw_10 *rw10;
291a6fd99a7Suwe 
292a6fd99a7Suwe 	if (xs->cmdlen == 6) {
293664c6166Skrw 		rw = (struct scsi_rw *)&xs->cmd;
294a6fd99a7Suwe 		*blocknop = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff);
295a6fd99a7Suwe 		*blockcntp = rw->length ? rw->length : 0x100;
296a6fd99a7Suwe 	} else {
297664c6166Skrw 		rw10 = (struct scsi_rw_10 *)&xs->cmd;
298eccd596dSkrw 		*blocknop = _4btol(rw10->addr);
299eccd596dSkrw 		*blockcntp = _2btol(rw10->length);
300a6fd99a7Suwe 	}
301a6fd99a7Suwe }
302a6fd99a7Suwe 
303bae03be6Skrw void
sdmmc_scsi_cmd(struct scsi_xfer * xs)304aae4fe77Suwe sdmmc_scsi_cmd(struct scsi_xfer *xs)
305aae4fe77Suwe {
306aae4fe77Suwe 	struct scsi_link *link = xs->sc_link;
3070b29cb40Skrw 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
308aae4fe77Suwe 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
309aae4fe77Suwe 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
310aae4fe77Suwe 	struct scsi_read_cap_data rcd;
311a6fd99a7Suwe 	u_int32_t blockno;
312a6fd99a7Suwe 	u_int32_t blockcnt;
313a6fd99a7Suwe 	struct sdmmc_ccb *ccb;
314aae4fe77Suwe 
315aae4fe77Suwe 	if (link->target >= scbus->sc_ntargets || tgt->card == NULL ||
316aae4fe77Suwe 	    link->lun != 0) {
317aae4fe77Suwe 		DPRINTF(("%s: sdmmc_scsi_cmd: no target %d\n",
318a6fd99a7Suwe 		    DEVNAME(sc), link->target));
319aae4fe77Suwe 		/* XXX should be XS_SENSE and sense filled out */
320aae4fe77Suwe 		xs->error = XS_DRIVER_STUFFUP;
321aae4fe77Suwe 		scsi_done(xs);
322bae03be6Skrw 		return;
323aae4fe77Suwe 	}
324aae4fe77Suwe 
325a6fd99a7Suwe 	DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)\n",
3269949a151Skn 	    DEVNAME(sc), link->target, xs->cmd.opcode, curproc ?
3278fda72b7Sguenther 	    curproc->p_p->ps_comm : "", xs->flags & SCSI_POLL));
328aae4fe77Suwe 
329aae4fe77Suwe 	xs->error = XS_NOERROR;
330aae4fe77Suwe 
331664c6166Skrw 	switch (xs->cmd.opcode) {
332aae4fe77Suwe 	case READ_COMMAND:
333eccd596dSkrw 	case READ_10:
334aae4fe77Suwe 	case WRITE_COMMAND:
335eccd596dSkrw 	case WRITE_10:
336aae4fe77Suwe 		/* Deal with I/O outside the switch. */
337aae4fe77Suwe 		break;
338aae4fe77Suwe 
339aae4fe77Suwe 	case INQUIRY:
3401b59d03aSdlg 		sdmmc_inquiry(xs);
341bae03be6Skrw 		return;
342aae4fe77Suwe 
343aae4fe77Suwe 	case TEST_UNIT_READY:
344aae4fe77Suwe 	case START_STOP:
345aae4fe77Suwe 	case SYNCHRONIZE_CACHE:
346a843afa4Sdlg 		scsi_done(xs);
347bae03be6Skrw 		return;
348aae4fe77Suwe 
349aae4fe77Suwe 	case READ_CAPACITY:
350aae4fe77Suwe 		bzero(&rcd, sizeof rcd);
351aae4fe77Suwe 		_lto4b(tgt->card->csd.capacity - 1, rcd.addr);
352aae4fe77Suwe 		_lto4b(tgt->card->csd.sector_size, rcd.length);
353aae4fe77Suwe 		bcopy(&rcd, xs->data, MIN(xs->datalen, sizeof rcd));
354aae4fe77Suwe 		scsi_done(xs);
355bae03be6Skrw 		return;
356aae4fe77Suwe 
357aae4fe77Suwe 	default:
358aae4fe77Suwe 		DPRINTF(("%s: unsupported scsi command %#x\n",
359664c6166Skrw 		    DEVNAME(sc), xs->cmd.opcode));
360aae4fe77Suwe 		xs->error = XS_DRIVER_STUFFUP;
361aae4fe77Suwe 		scsi_done(xs);
362bae03be6Skrw 		return;
363aae4fe77Suwe 	}
364aae4fe77Suwe 
365a6fd99a7Suwe 	/* A read or write operation. */
366a6fd99a7Suwe 	sdmmc_scsi_decode_rw(xs, &blockno, &blockcnt);
367aae4fe77Suwe 
368a6fd99a7Suwe 	if (blockno >= tgt->card->csd.capacity ||
369a6fd99a7Suwe 	    blockno + blockcnt > tgt->card->csd.capacity) {
370a6fd99a7Suwe 		DPRINTF(("%s: out of bounds %u-%u >= %u\n", DEVNAME(sc),
371a6fd99a7Suwe 		    blockno, blockcnt, tgt->card->csd.capacity));
372a6fd99a7Suwe 		xs->error = XS_DRIVER_STUFFUP;
373a6fd99a7Suwe 		scsi_done(xs);
374bae03be6Skrw 		return;
375a6fd99a7Suwe 	}
376a6fd99a7Suwe 
377abf17191Skrw 	ccb = xs->io;
378a6fd99a7Suwe 
379a6fd99a7Suwe 	ccb->ccb_xs = xs;
380a6fd99a7Suwe 	ccb->ccb_blockcnt = blockcnt;
381a6fd99a7Suwe 	ccb->ccb_blockno = blockno;
382a6fd99a7Suwe 
383bae03be6Skrw 	sdmmc_start_xs(sc, ccb);
384aae4fe77Suwe }
385aae4fe77Suwe 
386bae03be6Skrw void
sdmmc_inquiry(struct scsi_xfer * xs)3871b59d03aSdlg sdmmc_inquiry(struct scsi_xfer *xs)
3881b59d03aSdlg {
3891b59d03aSdlg 	struct scsi_link *link = xs->sc_link;
3900b29cb40Skrw 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
391ed7b9048Skettenis 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
392ed7b9048Skettenis 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
3931b59d03aSdlg 	struct scsi_inquiry_data inq;
394664c6166Skrw 	struct scsi_inquiry *cdb = (struct scsi_inquiry *)&xs->cmd;
395ed7b9048Skettenis 	char vendor[sizeof(inq.vendor) + 1];
396ed7b9048Skettenis 	char product[sizeof(inq.product) + 1];
397ed7b9048Skettenis 	char revision[sizeof(inq.revision) + 1];
3981b59d03aSdlg 
3991b59d03aSdlg         if (xs->cmdlen != sizeof(*cdb)) {
4001b59d03aSdlg 		xs->error = XS_DRIVER_STUFFUP;
4011b59d03aSdlg 		goto done;
4021b59d03aSdlg 	}
4031b59d03aSdlg 
4041b59d03aSdlg 	if (ISSET(cdb->flags, SI_EVPD)) {
4051b59d03aSdlg 		xs->error = XS_DRIVER_STUFFUP;
4061b59d03aSdlg 		goto done;
4071b59d03aSdlg 	}
4081b59d03aSdlg 
409ed7b9048Skettenis 	memset(vendor, 0, sizeof(vendor));
410ed7b9048Skettenis 	memset(product, 0, sizeof(product));
411ed7b9048Skettenis 	memset(revision, 0, sizeof(revision));
412ed7b9048Skettenis 	switch (tgt->card->cid.mid) {
413ed7b9048Skettenis 	case 0x02:
4145deea848Sdlg 	case 0x03:
415ed7b9048Skettenis 	case 0x45:
416ed7b9048Skettenis 		strlcpy(vendor, "Sandisk", sizeof(vendor));
417ed7b9048Skettenis 		break;
418ed7b9048Skettenis 	case 0x11:
419ed7b9048Skettenis 		strlcpy(vendor, "Toshiba", sizeof(vendor));
420ed7b9048Skettenis 		break;
421ed7b9048Skettenis 	case 0x13:
422ed7b9048Skettenis 		strlcpy(vendor, "Micron", sizeof(vendor));
423ed7b9048Skettenis 		break;
424ed7b9048Skettenis 	case 0x15:
425ed7b9048Skettenis 		strlcpy(vendor, "Samsung", sizeof(vendor));
426ed7b9048Skettenis 		break;
4275deea848Sdlg 	case 0x27:
4285deea848Sdlg 		strlcpy(vendor, "Apacer", sizeof(vendor));
4295deea848Sdlg 		break;
430ed7b9048Skettenis 	case 0x70:
431ed7b9048Skettenis 		strlcpy(vendor, "Kingston", sizeof(vendor));
432ed7b9048Skettenis 		break;
4335deea848Sdlg 	case 0x90:
4345deea848Sdlg 		strlcpy(vendor, "Hynix", sizeof(vendor));
4355deea848Sdlg 		break;
436ed7b9048Skettenis 	default:
437ed7b9048Skettenis 		strlcpy(vendor, "SD/MMC", sizeof(vendor));
438ed7b9048Skettenis 		break;
439ed7b9048Skettenis 	}
440ed7b9048Skettenis 	strlcpy(product, tgt->card->cid.pnm, sizeof(product));
441ed7b9048Skettenis 	snprintf(revision, sizeof(revision), "%04X", tgt->card->cid.rev);
442ed7b9048Skettenis 
443ed7b9048Skettenis 	memset(&inq, 0, sizeof inq);
4441b59d03aSdlg 	inq.device = T_DIRECT;
445*10d2c6b3Sdlg 	if (!ISSET(sc->sc_caps, SMC_CAPS_NONREMOVABLE))
446c4758de4Sderaadt 		inq.dev_qual2 = SID_REMOVABLE;
447b291b595Skrw 	inq.version = SCSI_REV_2;
448bb4b71ebSkrw 	inq.response_format = SID_SCSI2_RESPONSE;
4493ca8dabfSkrw 	inq.additional_length = SID_SCSI2_ALEN;
450ed7b9048Skettenis 	memcpy(inq.vendor, vendor, sizeof(inq.vendor));
451ed7b9048Skettenis 	memcpy(inq.product, product, sizeof(inq.product));
452ed7b9048Skettenis 	memcpy(inq.revision, revision, sizeof(inq.revision));
4531b59d03aSdlg 
454a83ec286Skrw 	scsi_copy_internal_data(xs, &inq, sizeof(inq));
4551b59d03aSdlg 
4561b59d03aSdlg done:
4571b59d03aSdlg 	scsi_done(xs);
4581b59d03aSdlg }
4591b59d03aSdlg 
4601b59d03aSdlg void
sdmmc_start_xs(struct sdmmc_softc * sc,struct sdmmc_ccb * ccb)461a6fd99a7Suwe sdmmc_start_xs(struct sdmmc_softc *sc, struct sdmmc_ccb *ccb)
462aae4fe77Suwe {
463a6fd99a7Suwe 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
464a6fd99a7Suwe 	struct scsi_xfer *xs = ccb->ccb_xs;
465a6fd99a7Suwe 	int s;
466a6fd99a7Suwe 
467a6fd99a7Suwe 	timeout_set(&xs->stimeout, sdmmc_stimeout, ccb);
468a6fd99a7Suwe 	sdmmc_init_task(&ccb->ccb_task, sdmmc_complete_xs, ccb);
469a6fd99a7Suwe 
470a6fd99a7Suwe 	s = splbio();
471a6fd99a7Suwe 	TAILQ_INSERT_TAIL(&scbus->sc_ccb_runq, ccb, ccb_link);
472a6fd99a7Suwe 	ccb->ccb_state = SDMMC_CCB_QUEUED;
473a6fd99a7Suwe 	splx(s);
474a6fd99a7Suwe 
475a6fd99a7Suwe 	if (ISSET(xs->flags, SCSI_POLL)) {
476a6fd99a7Suwe 		sdmmc_complete_xs(ccb);
477bae03be6Skrw 		return;
478aae4fe77Suwe 	}
479aae4fe77Suwe 
480668304adSgrange 	timeout_add_msec(&xs->stimeout, xs->timeout);
481a6fd99a7Suwe 	sdmmc_add_task(sc, &ccb->ccb_task);
482aae4fe77Suwe }
483aae4fe77Suwe 
484a6fd99a7Suwe void
sdmmc_complete_xs(void * arg)485a6fd99a7Suwe sdmmc_complete_xs(void *arg)
486a6fd99a7Suwe {
487a6fd99a7Suwe 	struct sdmmc_ccb *ccb = arg;
488a6fd99a7Suwe 	struct scsi_xfer *xs = ccb->ccb_xs;
489a6fd99a7Suwe 	struct scsi_link *link = xs->sc_link;
4900b29cb40Skrw 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
491a6fd99a7Suwe 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
492a6fd99a7Suwe 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
493a6fd99a7Suwe 	int error;
494a6fd99a7Suwe 	int s;
495a6fd99a7Suwe 
496a6fd99a7Suwe 	DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)"
497664c6166Skrw 	    " complete\n", DEVNAME(sc), link->target, xs->cmd.opcode,
4988fda72b7Sguenther 	    curproc ? curproc->p_p->ps_comm : "", xs->flags & SCSI_POLL));
499a6fd99a7Suwe 
500a6fd99a7Suwe 	s = splbio();
501a6fd99a7Suwe 
502aae4fe77Suwe 	if (ISSET(xs->flags, SCSI_DATA_IN))
503a6fd99a7Suwe 		error = sdmmc_mem_read_block(tgt->card, ccb->ccb_blockno,
504a6fd99a7Suwe 		    xs->data, ccb->ccb_blockcnt * DEV_BSIZE);
505aae4fe77Suwe 	else
506a6fd99a7Suwe 		error = sdmmc_mem_write_block(tgt->card, ccb->ccb_blockno,
507a6fd99a7Suwe 		    xs->data, ccb->ccb_blockcnt * DEV_BSIZE);
508a6fd99a7Suwe 
509aae4fe77Suwe 	if (error != 0)
510aae4fe77Suwe 		xs->error = XS_DRIVER_STUFFUP;
511aae4fe77Suwe 
51270544baaSmk 	sdmmc_done_xs(ccb);
513a6fd99a7Suwe 	splx(s);
514a6fd99a7Suwe }
515a6fd99a7Suwe 
516a6fd99a7Suwe void
sdmmc_done_xs(struct sdmmc_ccb * ccb)517a6fd99a7Suwe sdmmc_done_xs(struct sdmmc_ccb *ccb)
518a6fd99a7Suwe {
519a6fd99a7Suwe 	struct scsi_xfer *xs = ccb->ccb_xs;
520a6fd99a7Suwe #ifdef SDMMC_DEBUG
521a6fd99a7Suwe 	struct scsi_link *link = xs->sc_link;
5220b29cb40Skrw 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
523a6fd99a7Suwe #endif
524a6fd99a7Suwe 
525a6fd99a7Suwe 	timeout_del(&xs->stimeout);
526a6fd99a7Suwe 
527a6fd99a7Suwe 	DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (error=%#x)"
528664c6166Skrw 	    " done\n", DEVNAME(sc), link->target, xs->cmd.opcode,
5298fda72b7Sguenther 	    curproc ? curproc->p_p->ps_comm : "", xs->error));
530a6fd99a7Suwe 
531aae4fe77Suwe 	xs->resid = 0;
532a6fd99a7Suwe 
533a6fd99a7Suwe 	if (ISSET(ccb->ccb_flags, SDMMC_CCB_F_ERR))
534a6fd99a7Suwe 		xs->error = XS_DRIVER_STUFFUP;
535a6fd99a7Suwe 
536aae4fe77Suwe 	scsi_done(xs);
537aae4fe77Suwe }
538aae4fe77Suwe 
539aae4fe77Suwe void
sdmmc_stimeout(void * arg)540a6fd99a7Suwe sdmmc_stimeout(void *arg)
541a6fd99a7Suwe {
542a6fd99a7Suwe 	struct sdmmc_ccb *ccb = arg;
543a6fd99a7Suwe 	int s;
544a6fd99a7Suwe 
545a6fd99a7Suwe 	s = splbio();
546a6fd99a7Suwe 	ccb->ccb_flags |= SDMMC_CCB_F_ERR;
547a6fd99a7Suwe 	if (sdmmc_task_pending(&ccb->ccb_task)) {
548a6fd99a7Suwe 		sdmmc_del_task(&ccb->ccb_task);
54970544baaSmk 		sdmmc_done_xs(ccb);
550a6fd99a7Suwe 	}
551a6fd99a7Suwe 	splx(s);
552a6fd99a7Suwe }
553a6fd99a7Suwe 
554a6fd99a7Suwe void
sdmmc_minphys(struct buf * bp,struct scsi_link * sl)5559f36bc94Skrw sdmmc_minphys(struct buf *bp, struct scsi_link *sl)
556aae4fe77Suwe {
5570b29cb40Skrw 	struct sdmmc_softc *sc = sl->bus->sb_adapter_softc;
55846a44037Smiod 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
55946a44037Smiod 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[sl->target];
56046a44037Smiod 	struct sdmmc_function *sf = tgt->card;
56146a44037Smiod 
56246a44037Smiod 	/* limit to max. transfer size supported by card/host */
56346a44037Smiod 	if (sc->sc_max_xfer != 0 &&
56446a44037Smiod 	    bp->b_bcount > sf->csd.sector_size * sc->sc_max_xfer)
56546a44037Smiod 		bp->b_bcount = sf->csd.sector_size * sc->sc_max_xfer;
5667f649021Skrw 	else
5677f649021Skrw 		minphys(bp);
568aae4fe77Suwe }
569f3036462Sjmatthew 
570f3036462Sjmatthew #ifdef HIBERNATE
571f3036462Sjmatthew int
sdmmc_scsi_hibernate_io(dev_t dev,daddr_t blkno,vaddr_t addr,size_t size,int op,void * page)572f3036462Sjmatthew sdmmc_scsi_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size,
573f3036462Sjmatthew     int op, void *page)
574f3036462Sjmatthew {
575f3036462Sjmatthew 	struct {
576f3036462Sjmatthew 		struct sdmmc_softc sdmmc_sc;
577f3036462Sjmatthew 		struct sdmmc_function sdmmc_sf;
578f3036462Sjmatthew 		daddr_t poffset;
579f3036462Sjmatthew 		size_t psize;
580f3036462Sjmatthew 		struct sdmmc_function *orig_sf;
581f3036462Sjmatthew 		char chipset_softc[0];	/* size depends on the chipset layer */
582f3036462Sjmatthew 	} *state = page;
583f3036462Sjmatthew 	extern struct cfdriver sd_cd;
584f3036462Sjmatthew 	struct device *disk, *scsibus, *chip, *sdmmc;
585f3036462Sjmatthew 	struct scsibus_softc *bus_sc;
586f3036462Sjmatthew 	struct sdmmc_scsi_softc *scsi_sc;
587f3036462Sjmatthew 	struct scsi_link *link;
588f3036462Sjmatthew 	struct sdmmc_function *sf;
589f3036462Sjmatthew 	struct sdmmc_softc *sc;
590f3036462Sjmatthew 	int error;
591f3036462Sjmatthew 
592f3036462Sjmatthew 	switch (op) {
593f3036462Sjmatthew 	case HIB_INIT:
594f3036462Sjmatthew 		/* find device (sdmmc_softc, sdmmc_function) */
595f3036462Sjmatthew 		disk = disk_lookup(&sd_cd, DISKUNIT(dev));
5964b20f930Sjmatthew 		if (disk == NULL)
5974b20f930Sjmatthew 			return (ENOTTY);
5984b20f930Sjmatthew 
599f3036462Sjmatthew 		scsibus = disk->dv_parent;
600f3036462Sjmatthew 		sdmmc = scsibus->dv_parent;
601f3036462Sjmatthew 		chip = sdmmc->dv_parent;
602f3036462Sjmatthew 
603f3036462Sjmatthew 		bus_sc = (struct scsibus_softc *)scsibus;
604f3036462Sjmatthew 		scsi_sc = (struct sdmmc_scsi_softc *)scsibus;
6054b20f930Sjmatthew 		sc = NULL;
606f3036462Sjmatthew 		SLIST_FOREACH(link, &bus_sc->sc_link_list, bus_list) {
607f3036462Sjmatthew 			if (link->device_softc == disk) {
6080b29cb40Skrw 				sc = link->bus->sb_adapter_softc;
609f3036462Sjmatthew 				scsi_sc = sc->sc_scsibus;
610f3036462Sjmatthew 				sf = scsi_sc->sc_tgt[link->target].card;
611f3036462Sjmatthew 			}
612f3036462Sjmatthew 		}
6134b20f930Sjmatthew 		if (sc == NULL || sf == NULL)
6144b20f930Sjmatthew 			return (ENOTTY);
615f3036462Sjmatthew 
616f3036462Sjmatthew 		/* if the chipset doesn't do hibernate, bail out now */
617f3036462Sjmatthew 		sc = (struct sdmmc_softc *)sdmmc;
618f3036462Sjmatthew 		if (sc->sct->hibernate_init == NULL)
619f3036462Sjmatthew 			return (ENOTTY);
620f3036462Sjmatthew 
621f3036462Sjmatthew 		state->sdmmc_sc = *sc;
622f3036462Sjmatthew 		state->sdmmc_sf = *sf;
623f3036462Sjmatthew 		state->sdmmc_sf.sc = &state->sdmmc_sc;
624f3036462Sjmatthew 
625f3036462Sjmatthew 		/* pretend we own the lock */
626f3036462Sjmatthew 		state->sdmmc_sc.sc_lock.rwl_owner =
627f3036462Sjmatthew 		    (((long)curproc) & ~RWLOCK_MASK) | RWLOCK_WRLOCK;
628f3036462Sjmatthew 
629f3036462Sjmatthew 		/* build chip layer fake softc */
630f3036462Sjmatthew 		error = state->sdmmc_sc.sct->hibernate_init(state->sdmmc_sc.sch,
631f3036462Sjmatthew 		    &state->chipset_softc);
632f3036462Sjmatthew 		if (error)
633f3036462Sjmatthew 			return (error);
634f3036462Sjmatthew 		state->sdmmc_sc.sch = state->chipset_softc;
635f3036462Sjmatthew 
636f3036462Sjmatthew 		/* make sure we're talking to the right target */
637f3036462Sjmatthew 		state->orig_sf = sc->sc_card;
638f3036462Sjmatthew 		error = sdmmc_select_card(&state->sdmmc_sc, &state->sdmmc_sf);
639f3036462Sjmatthew 		if (error)
640f3036462Sjmatthew 			return (error);
641f3036462Sjmatthew 
642f3036462Sjmatthew 		state->poffset = blkno;
643f3036462Sjmatthew 		state->psize = size;
644f3036462Sjmatthew 		return (0);
645f3036462Sjmatthew 
646f3036462Sjmatthew 	case HIB_W:
647f3036462Sjmatthew 		if (blkno > state->psize)
648f3036462Sjmatthew 			return (E2BIG);
649f3036462Sjmatthew 		return (sdmmc_mem_hibernate_write(&state->sdmmc_sf,
650f3036462Sjmatthew 		    blkno + state->poffset, (u_char *)addr, size));
651f3036462Sjmatthew 
652f3036462Sjmatthew 	case HIB_DONE:
653f3036462Sjmatthew 		/*
654f3036462Sjmatthew 		 * bring the hardware state back into line with the real
655f3036462Sjmatthew 		 * softc by operating on the fake one
656f3036462Sjmatthew 		 */
6574b20f930Sjmatthew 		return (sdmmc_select_card(&state->sdmmc_sc, state->orig_sf));
658f3036462Sjmatthew 	}
659f3036462Sjmatthew 
660f3036462Sjmatthew 	return (EINVAL);
661f3036462Sjmatthew }
662f3036462Sjmatthew 
663f3036462Sjmatthew #endif
664