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