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