xref: /openbsd-src/sys/dev/sdmmc/sdmmc_scsi.c (revision 505ee9ea3b177e2387d907a91ca7da069f3f14d8)
1 /*	$OpenBSD: sdmmc_scsi.c,v 1.51 2020/07/20 14:41:14 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* A SCSI adapter emulation to access SD/MMC memory cards */
20 
21 #include <sys/param.h>
22 #include <sys/buf.h>
23 #include <sys/device.h>
24 #include <sys/malloc.h>
25 #include <sys/proc.h>
26 #include <sys/systm.h>
27 
28 #include <scsi/scsi_all.h>
29 #include <scsi/scsi_disk.h>
30 #include <scsi/scsiconf.h>
31 
32 #include <dev/sdmmc/sdmmc_scsi.h>
33 #include <dev/sdmmc/sdmmcvar.h>
34 
35 #ifdef HIBERNATE
36 #include <sys/hibernate.h>
37 #include <sys/disk.h>
38 #include <sys/disklabel.h>
39 #include <sys/rwlock.h>
40 #endif
41 
42 #define SDMMC_SCSIID_HOST	0x00
43 #define SDMMC_SCSIID_MAX	0x0f
44 
45 #define SDMMC_SCSI_MAXCMDS	8
46 
47 struct sdmmc_scsi_target {
48 	struct sdmmc_function *card;
49 };
50 
51 struct sdmmc_ccb {
52 	struct sdmmc_scsi_softc *ccb_scbus;
53 	struct scsi_xfer *ccb_xs;
54 	int ccb_flags;
55 #define SDMMC_CCB_F_ERR		0x0001
56 	u_int32_t ccb_blockno;
57 	u_int32_t ccb_blockcnt;
58 	volatile enum {
59 		SDMMC_CCB_FREE,
60 		SDMMC_CCB_READY,
61 		SDMMC_CCB_QUEUED
62 	} ccb_state;
63 	struct sdmmc_command ccb_cmd;
64 	struct sdmmc_task ccb_task;
65 	TAILQ_ENTRY(sdmmc_ccb) ccb_link;
66 };
67 
68 TAILQ_HEAD(sdmmc_ccb_list, sdmmc_ccb);
69 
70 struct sdmmc_scsi_softc {
71 	struct scsi_link sc_link;
72 	struct device *sc_child;
73 	struct sdmmc_scsi_target *sc_tgt;
74 	int sc_ntargets;
75 	struct sdmmc_ccb *sc_ccbs;		/* allocated ccbs */
76 	int		sc_nccbs;
77 	struct sdmmc_ccb_list sc_ccb_freeq;	/* free ccbs */
78 	struct sdmmc_ccb_list sc_ccb_runq;	/* queued ccbs */
79 	struct mutex sc_ccb_mtx;
80 	struct scsi_iopool sc_iopool;
81 };
82 
83 int	sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *, int);
84 void	sdmmc_free_ccbs(struct sdmmc_scsi_softc *);
85 void	*sdmmc_ccb_alloc(void *);
86 void	sdmmc_ccb_free(void *, void *);
87 
88 void	sdmmc_scsi_cmd(struct scsi_xfer *);
89 void	sdmmc_inquiry(struct scsi_xfer *);
90 void	sdmmc_start_xs(struct sdmmc_softc *, struct sdmmc_ccb *);
91 void	sdmmc_complete_xs(void *);
92 void	sdmmc_done_xs(struct sdmmc_ccb *);
93 void	sdmmc_stimeout(void *);
94 void	sdmmc_minphys(struct buf *, struct scsi_link *);
95 
96 struct scsi_adapter sdmmc_switch = {
97 	sdmmc_scsi_cmd, sdmmc_minphys, NULL, NULL, NULL
98 };
99 
100 #ifdef SDMMC_DEBUG
101 #define DPRINTF(s)	printf s
102 #else
103 #define DPRINTF(s)	/**/
104 #endif
105 
106 void
107 sdmmc_scsi_attach(struct sdmmc_softc *sc)
108 {
109 	struct sdmmc_attach_args saa;
110 	struct sdmmc_scsi_softc *scbus;
111 	struct sdmmc_function *sf;
112 
113 	rw_assert_wrlock(&sc->sc_lock);
114 
115 	scbus = malloc(sizeof *scbus, M_DEVBUF, M_WAITOK | M_ZERO);
116 
117 	scbus->sc_tgt = mallocarray(sizeof(*scbus->sc_tgt),
118 	    (SDMMC_SCSIID_MAX+1), M_DEVBUF, M_WAITOK | M_ZERO);
119 
120 	/*
121 	 * Each card that sent us a CID in the identification stage
122 	 * gets a SCSI ID > 0, whether it is a memory card or not.
123 	 */
124 	scbus->sc_ntargets = 1;
125 	SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
126 		if (scbus->sc_ntargets >= SDMMC_SCSIID_MAX+1)
127 			break;
128 		scbus->sc_tgt[scbus->sc_ntargets].card = sf;
129 		scbus->sc_ntargets++;
130 	}
131 
132 	/* Preallocate some CCBs and initialize the CCB lists. */
133 	if (sdmmc_alloc_ccbs(scbus, SDMMC_SCSI_MAXCMDS) != 0) {
134 		printf("%s: can't allocate ccbs\n", sc->sc_dev.dv_xname);
135 		goto free_sctgt;
136 	}
137 
138 	sc->sc_scsibus = scbus;
139 
140 	bzero(&saa, sizeof(saa));
141 	saa.saa.saa_adapter_target = SDMMC_SCSIID_HOST;
142 	saa.saa.saa_adapter_buswidth = scbus->sc_ntargets;
143 	saa.saa.saa_adapter_softc = sc;
144 	saa.saa.saa_luns = 1;
145 	saa.saa.saa_adapter = &sdmmc_switch;
146 	saa.saa.saa_openings = 1;
147 	saa.saa.saa_pool = &scbus->sc_iopool;
148 	saa.saa.saa_quirks = saa.saa.saa_flags = 0;
149 	saa.saa.saa_wwpn = saa.saa.saa_wwnn = 0;
150 
151 	scbus->sc_child = config_found(&sc->sc_dev, &saa, scsiprint);
152 	if (scbus->sc_child == NULL) {
153 		printf("%s: can't attach scsibus\n", sc->sc_dev.dv_xname);
154 		goto free_ccbs;
155 	}
156 	return;
157 
158  free_ccbs:
159 	sc->sc_scsibus = NULL;
160 	sdmmc_free_ccbs(scbus);
161  free_sctgt:
162 	free(scbus->sc_tgt, M_DEVBUF,
163 	    sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1));
164 	free(scbus, M_DEVBUF, sizeof *scbus);
165 }
166 
167 void
168 sdmmc_scsi_detach(struct sdmmc_softc *sc)
169 {
170 	struct sdmmc_scsi_softc *scbus;
171 	struct sdmmc_ccb *ccb;
172 	int s;
173 
174 	rw_assert_wrlock(&sc->sc_lock);
175 
176 	scbus = sc->sc_scsibus;
177 	if (scbus == NULL)
178 		return;
179 
180 	/* Complete all open scsi xfers. */
181 	s = splbio();
182 	for (ccb = TAILQ_FIRST(&scbus->sc_ccb_runq); ccb != NULL;
183 	     ccb = TAILQ_FIRST(&scbus->sc_ccb_runq))
184 		sdmmc_stimeout(ccb);
185 	splx(s);
186 
187 	if (scbus->sc_child != NULL)
188 		config_detach(scbus->sc_child, DETACH_FORCE);
189 
190 	if (scbus->sc_tgt != NULL)
191 		free(scbus->sc_tgt, M_DEVBUF,
192 		    sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1));
193 
194 	sdmmc_free_ccbs(scbus);
195 	free(scbus, M_DEVBUF, sizeof *scbus);
196 	sc->sc_scsibus = NULL;
197 }
198 
199 /*
200  * CCB management
201  */
202 
203 int
204 sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *scbus, int nccbs)
205 {
206 	struct sdmmc_ccb *ccb;
207 	int i;
208 
209 	scbus->sc_ccbs = mallocarray(nccbs, sizeof(struct sdmmc_ccb),
210 	    M_DEVBUF, M_NOWAIT);
211 	if (scbus->sc_ccbs == NULL)
212 		return 1;
213 	scbus->sc_nccbs = nccbs;
214 
215 	TAILQ_INIT(&scbus->sc_ccb_freeq);
216 	TAILQ_INIT(&scbus->sc_ccb_runq);
217 	mtx_init(&scbus->sc_ccb_mtx, IPL_BIO);
218 	scsi_iopool_init(&scbus->sc_iopool, scbus, sdmmc_ccb_alloc,
219 	    sdmmc_ccb_free);
220 
221 	for (i = 0; i < nccbs; i++) {
222 		ccb = &scbus->sc_ccbs[i];
223 		ccb->ccb_scbus = scbus;
224 		ccb->ccb_state = SDMMC_CCB_FREE;
225 		ccb->ccb_flags = 0;
226 		ccb->ccb_xs = NULL;
227 
228 		TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link);
229 	}
230 	return 0;
231 }
232 
233 void
234 sdmmc_free_ccbs(struct sdmmc_scsi_softc *scbus)
235 {
236 	if (scbus->sc_ccbs != NULL) {
237 		free(scbus->sc_ccbs, M_DEVBUF,
238 		    scbus->sc_nccbs * sizeof(struct sdmmc_ccb));
239 		scbus->sc_ccbs = NULL;
240 	}
241 }
242 
243 void *
244 sdmmc_ccb_alloc(void *xscbus)
245 {
246 	struct sdmmc_scsi_softc *scbus = xscbus;
247 	struct sdmmc_ccb *ccb;
248 
249 	mtx_enter(&scbus->sc_ccb_mtx);
250 	ccb = TAILQ_FIRST(&scbus->sc_ccb_freeq);
251 	if (ccb != NULL) {
252 		TAILQ_REMOVE(&scbus->sc_ccb_freeq, ccb, ccb_link);
253 		ccb->ccb_state = SDMMC_CCB_READY;
254 	}
255 	mtx_leave(&scbus->sc_ccb_mtx);
256 
257 	return ccb;
258 }
259 
260 void
261 sdmmc_ccb_free(void *xscbus, void *xccb)
262 {
263 	struct sdmmc_scsi_softc *scbus = xscbus;
264 	struct sdmmc_ccb *ccb = xccb;
265 	int s;
266 
267 	s = splbio();
268 	if (ccb->ccb_state == SDMMC_CCB_QUEUED)
269 		TAILQ_REMOVE(&scbus->sc_ccb_runq, ccb, ccb_link);
270 	splx(s);
271 
272 	ccb->ccb_state = SDMMC_CCB_FREE;
273 	ccb->ccb_flags = 0;
274 	ccb->ccb_xs = NULL;
275 
276 	mtx_enter(&scbus->sc_ccb_mtx);
277 	TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link);
278 	mtx_leave(&scbus->sc_ccb_mtx);
279 }
280 
281 /*
282  * SCSI command emulation
283  */
284 
285 /* XXX move to some sort of "scsi emulation layer". */
286 static void
287 sdmmc_scsi_decode_rw(struct scsi_xfer *xs, u_int32_t *blocknop,
288     u_int32_t *blockcntp)
289 {
290 	struct scsi_rw *rw;
291 	struct scsi_rw_big *rwb;
292 
293 	if (xs->cmdlen == 6) {
294 		rw = (struct scsi_rw *)xs->cmd;
295 		*blocknop = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff);
296 		*blockcntp = rw->length ? rw->length : 0x100;
297 	} else {
298 		rwb = (struct scsi_rw_big *)xs->cmd;
299 		*blocknop = _4btol(rwb->addr);
300 		*blockcntp = _2btol(rwb->length);
301 	}
302 }
303 
304 void
305 sdmmc_scsi_cmd(struct scsi_xfer *xs)
306 {
307 	struct scsi_link *link = xs->sc_link;
308 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
309 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
310 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
311 	struct scsi_read_cap_data rcd;
312 	u_int32_t blockno;
313 	u_int32_t blockcnt;
314 	struct sdmmc_ccb *ccb;
315 
316 	if (link->target >= scbus->sc_ntargets || tgt->card == NULL ||
317 	    link->lun != 0) {
318 		DPRINTF(("%s: sdmmc_scsi_cmd: no target %d\n",
319 		    DEVNAME(sc), link->target));
320 		/* XXX should be XS_SENSE and sense filled out */
321 		xs->error = XS_DRIVER_STUFFUP;
322 		scsi_done(xs);
323 		return;
324 	}
325 
326 	DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)\n",
327 	    DEVNAME(sc), link->target, xs->cmd->opcode, curproc ?
328 	    curproc->p_p->ps_comm : "", xs->flags & SCSI_POLL));
329 
330 	xs->error = XS_NOERROR;
331 
332 	switch (xs->cmd->opcode) {
333 	case READ_COMMAND:
334 	case READ_BIG:
335 	case WRITE_COMMAND:
336 	case WRITE_BIG:
337 		/* Deal with I/O outside the switch. */
338 		break;
339 
340 	case INQUIRY:
341 		sdmmc_inquiry(xs);
342 		return;
343 
344 	case TEST_UNIT_READY:
345 	case START_STOP:
346 	case SYNCHRONIZE_CACHE:
347 		scsi_done(xs);
348 		return;
349 
350 	case READ_CAPACITY:
351 		bzero(&rcd, sizeof rcd);
352 		_lto4b(tgt->card->csd.capacity - 1, rcd.addr);
353 		_lto4b(tgt->card->csd.sector_size, rcd.length);
354 		bcopy(&rcd, xs->data, MIN(xs->datalen, sizeof rcd));
355 		scsi_done(xs);
356 		return;
357 
358 	default:
359 		DPRINTF(("%s: unsupported scsi command %#x\n",
360 		    DEVNAME(sc), xs->cmd->opcode));
361 		xs->error = XS_DRIVER_STUFFUP;
362 		scsi_done(xs);
363 		return;
364 	}
365 
366 	/* A read or write operation. */
367 	sdmmc_scsi_decode_rw(xs, &blockno, &blockcnt);
368 
369 	if (blockno >= tgt->card->csd.capacity ||
370 	    blockno + blockcnt > tgt->card->csd.capacity) {
371 		DPRINTF(("%s: out of bounds %u-%u >= %u\n", DEVNAME(sc),
372 		    blockno, blockcnt, tgt->card->csd.capacity));
373 		xs->error = XS_DRIVER_STUFFUP;
374 		scsi_done(xs);
375 		return;
376 	}
377 
378 	ccb = xs->io;
379 
380 	ccb->ccb_xs = xs;
381 	ccb->ccb_blockcnt = blockcnt;
382 	ccb->ccb_blockno = blockno;
383 
384 	sdmmc_start_xs(sc, ccb);
385 }
386 
387 void
388 sdmmc_inquiry(struct scsi_xfer *xs)
389 {
390 	struct scsi_link *link = xs->sc_link;
391 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
392 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
393 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
394 	struct scsi_inquiry_data inq;
395 	struct scsi_inquiry *cdb = (struct scsi_inquiry *)xs->cmd;
396 	char vendor[sizeof(inq.vendor) + 1];
397 	char product[sizeof(inq.product) + 1];
398 	char revision[sizeof(inq.revision) + 1];
399 
400         if (xs->cmdlen != sizeof(*cdb)) {
401 		xs->error = XS_DRIVER_STUFFUP;
402 		goto done;
403 	}
404 
405 	if (ISSET(cdb->flags, SI_EVPD)) {
406 		xs->error = XS_DRIVER_STUFFUP;
407 		goto done;
408 	}
409 
410 	memset(vendor, 0, sizeof(vendor));
411 	memset(product, 0, sizeof(product));
412 	memset(revision, 0, sizeof(revision));
413 	switch (tgt->card->cid.mid) {
414 	case 0x02:
415 	case 0x45:
416 		strlcpy(vendor, "Sandisk", sizeof(vendor));
417 		break;
418 	case 0x11:
419 		strlcpy(vendor, "Toshiba", sizeof(vendor));
420 		break;
421 	case 0x13:
422 		strlcpy(vendor, "Micron", sizeof(vendor));
423 		break;
424 	case 0x15:
425 		strlcpy(vendor, "Samsung", sizeof(vendor));
426 		break;
427 	case 0x70:
428 		strlcpy(vendor, "Kingston", sizeof(vendor));
429 		break;
430 	default:
431 		strlcpy(vendor, "SD/MMC", sizeof(vendor));
432 		break;
433 	}
434 	strlcpy(product, tgt->card->cid.pnm, sizeof(product));
435 	snprintf(revision, sizeof(revision), "%04X", tgt->card->cid.rev);
436 
437 	memset(&inq, 0, sizeof inq);
438 	inq.device = T_DIRECT;
439 	inq.dev_qual2 = SID_REMOVABLE;
440 	inq.version = 2;
441 	inq.response_format = 2;
442 	inq.additional_length = 32;
443 	memcpy(inq.vendor, vendor, sizeof(inq.vendor));
444 	memcpy(inq.product, product, sizeof(inq.product));
445 	memcpy(inq.revision, revision, sizeof(inq.revision));
446 
447 	memcpy(xs->data, &inq, MIN(xs->datalen, sizeof(inq)));
448 
449 done:
450 	scsi_done(xs);
451 }
452 
453 void
454 sdmmc_start_xs(struct sdmmc_softc *sc, struct sdmmc_ccb *ccb)
455 {
456 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
457 	struct scsi_xfer *xs = ccb->ccb_xs;
458 	int s;
459 
460 	timeout_set(&xs->stimeout, sdmmc_stimeout, ccb);
461 	sdmmc_init_task(&ccb->ccb_task, sdmmc_complete_xs, ccb);
462 
463 	s = splbio();
464 	TAILQ_INSERT_TAIL(&scbus->sc_ccb_runq, ccb, ccb_link);
465 	ccb->ccb_state = SDMMC_CCB_QUEUED;
466 	splx(s);
467 
468 	if (ISSET(xs->flags, SCSI_POLL)) {
469 		sdmmc_complete_xs(ccb);
470 		return;
471 	}
472 
473 	timeout_add_msec(&xs->stimeout, xs->timeout);
474 	sdmmc_add_task(sc, &ccb->ccb_task);
475 }
476 
477 void
478 sdmmc_complete_xs(void *arg)
479 {
480 	struct sdmmc_ccb *ccb = arg;
481 	struct scsi_xfer *xs = ccb->ccb_xs;
482 	struct scsi_link *link = xs->sc_link;
483 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
484 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
485 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
486 	int error;
487 	int s;
488 
489 	DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)"
490 	    " complete\n", DEVNAME(sc), link->target, xs->cmd->opcode,
491 	    curproc ? curproc->p_p->ps_comm : "", xs->flags & SCSI_POLL));
492 
493 	s = splbio();
494 
495 	if (ISSET(xs->flags, SCSI_DATA_IN))
496 		error = sdmmc_mem_read_block(tgt->card, ccb->ccb_blockno,
497 		    xs->data, ccb->ccb_blockcnt * DEV_BSIZE);
498 	else
499 		error = sdmmc_mem_write_block(tgt->card, ccb->ccb_blockno,
500 		    xs->data, ccb->ccb_blockcnt * DEV_BSIZE);
501 
502 	if (error != 0)
503 		xs->error = XS_DRIVER_STUFFUP;
504 
505 	sdmmc_done_xs(ccb);
506 	splx(s);
507 }
508 
509 void
510 sdmmc_done_xs(struct sdmmc_ccb *ccb)
511 {
512 	struct scsi_xfer *xs = ccb->ccb_xs;
513 #ifdef SDMMC_DEBUG
514 	struct scsi_link *link = xs->sc_link;
515 	struct sdmmc_softc *sc = link->bus->sb_adapter_softc;
516 #endif
517 
518 	timeout_del(&xs->stimeout);
519 
520 	DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (error=%#x)"
521 	    " done\n", DEVNAME(sc), link->target, xs->cmd->opcode,
522 	    curproc ? curproc->p_p->ps_comm : "", xs->error));
523 
524 	xs->resid = 0;
525 
526 	if (ISSET(ccb->ccb_flags, SDMMC_CCB_F_ERR))
527 		xs->error = XS_DRIVER_STUFFUP;
528 
529 	scsi_done(xs);
530 }
531 
532 void
533 sdmmc_stimeout(void *arg)
534 {
535 	struct sdmmc_ccb *ccb = arg;
536 	int s;
537 
538 	s = splbio();
539 	ccb->ccb_flags |= SDMMC_CCB_F_ERR;
540 	if (sdmmc_task_pending(&ccb->ccb_task)) {
541 		sdmmc_del_task(&ccb->ccb_task);
542 		sdmmc_done_xs(ccb);
543 	}
544 	splx(s);
545 }
546 
547 void
548 sdmmc_minphys(struct buf *bp, struct scsi_link *sl)
549 {
550 	struct sdmmc_softc *sc = sl->bus->sb_adapter_softc;
551 	struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
552 	struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[sl->target];
553 	struct sdmmc_function *sf = tgt->card;
554 
555 	/* limit to max. transfer size supported by card/host */
556 	if (sc->sc_max_xfer != 0 &&
557 	    bp->b_bcount > sf->csd.sector_size * sc->sc_max_xfer)
558 		bp->b_bcount = sf->csd.sector_size * sc->sc_max_xfer;
559 	else
560 		minphys(bp);
561 }
562 
563 #ifdef HIBERNATE
564 int
565 sdmmc_scsi_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size,
566     int op, void *page)
567 {
568 	struct {
569 		struct sdmmc_softc sdmmc_sc;
570 		struct sdmmc_function sdmmc_sf;
571 		daddr_t poffset;
572 		size_t psize;
573 		struct sdmmc_function *orig_sf;
574 		char chipset_softc[0];	/* size depends on the chipset layer */
575 	} *state = page;
576 	extern struct cfdriver sd_cd;
577 	struct device *disk, *scsibus, *chip, *sdmmc;
578 	struct scsibus_softc *bus_sc;
579 	struct sdmmc_scsi_softc *scsi_sc;
580 	struct scsi_link *link;
581 	struct sdmmc_function *sf;
582 	struct sdmmc_softc *sc;
583 	int error;
584 
585 	switch (op) {
586 	case HIB_INIT:
587 		/* find device (sdmmc_softc, sdmmc_function) */
588 		disk = disk_lookup(&sd_cd, DISKUNIT(dev));
589 		if (disk == NULL)
590 			return (ENOTTY);
591 
592 		scsibus = disk->dv_parent;
593 		sdmmc = scsibus->dv_parent;
594 		chip = sdmmc->dv_parent;
595 
596 		bus_sc = (struct scsibus_softc *)scsibus;
597 		scsi_sc = (struct sdmmc_scsi_softc *)scsibus;
598 		sc = NULL;
599 		SLIST_FOREACH(link, &bus_sc->sc_link_list, bus_list) {
600 			if (link->device_softc == disk) {
601 				sc = link->bus->sb_adapter_softc;
602 				scsi_sc = sc->sc_scsibus;
603 				sf = scsi_sc->sc_tgt[link->target].card;
604 			}
605 		}
606 		if (sc == NULL || sf == NULL)
607 			return (ENOTTY);
608 
609 		/* if the chipset doesn't do hibernate, bail out now */
610 		sc = (struct sdmmc_softc *)sdmmc;
611 		if (sc->sct->hibernate_init == NULL)
612 			return (ENOTTY);
613 
614 		state->sdmmc_sc = *sc;
615 		state->sdmmc_sf = *sf;
616 		state->sdmmc_sf.sc = &state->sdmmc_sc;
617 
618 		/* pretend we own the lock */
619 		state->sdmmc_sc.sc_lock.rwl_owner =
620 		    (((long)curproc) & ~RWLOCK_MASK) | RWLOCK_WRLOCK;
621 
622 		/* build chip layer fake softc */
623 		error = state->sdmmc_sc.sct->hibernate_init(state->sdmmc_sc.sch,
624 		    &state->chipset_softc);
625 		if (error)
626 			return (error);
627 		state->sdmmc_sc.sch = state->chipset_softc;
628 
629 		/* make sure we're talking to the right target */
630 		state->orig_sf = sc->sc_card;
631 		error = sdmmc_select_card(&state->sdmmc_sc, &state->sdmmc_sf);
632 		if (error)
633 			return (error);
634 
635 		state->poffset = blkno;
636 		state->psize = size;
637 		return (0);
638 
639 	case HIB_W:
640 		if (blkno > state->psize)
641 			return (E2BIG);
642 		return (sdmmc_mem_hibernate_write(&state->sdmmc_sf,
643 		    blkno + state->poffset, (u_char *)addr, size));
644 
645 	case HIB_DONE:
646 		/*
647 		 * bring the hardware state back into line with the real
648 		 * softc by operating on the fake one
649 		 */
650 		return (sdmmc_select_card(&state->sdmmc_sc, state->orig_sf));
651 	}
652 
653 	return (EINVAL);
654 }
655 
656 #endif
657