Annotation of sys/dev/sdmmc/sdmmc_scsi.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: sdmmc_scsi.c,v 1.7 2006/11/28 23:59:45 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/kernel.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: #define SDMMC_SCSIID_HOST 0x00
! 36: #define SDMMC_SCSIID_MAX 0x0f
! 37:
! 38: #define SDMMC_SCSI_MAXCMDS 8
! 39:
! 40: struct sdmmc_scsi_target {
! 41: struct sdmmc_function *card;
! 42: };
! 43:
! 44: struct sdmmc_ccb {
! 45: struct sdmmc_scsi_softc *ccb_scbus;
! 46: struct scsi_xfer *ccb_xs;
! 47: int ccb_flags;
! 48: #define SDMMC_CCB_F_ERR 0x0001
! 49: void (*ccb_done)(struct sdmmc_ccb *);
! 50: u_int32_t ccb_blockno;
! 51: u_int32_t ccb_blockcnt;
! 52: volatile enum {
! 53: SDMMC_CCB_FREE,
! 54: SDMMC_CCB_READY,
! 55: SDMMC_CCB_QUEUED
! 56: } ccb_state;
! 57: struct sdmmc_command ccb_cmd;
! 58: struct sdmmc_task ccb_task;
! 59: TAILQ_ENTRY(sdmmc_ccb) ccb_link;
! 60: };
! 61:
! 62: TAILQ_HEAD(sdmmc_ccb_list, sdmmc_ccb);
! 63:
! 64: struct sdmmc_scsi_softc {
! 65: struct scsi_adapter sc_adapter;
! 66: struct scsi_link sc_link;
! 67: struct device *sc_child;
! 68: struct sdmmc_scsi_target *sc_tgt;
! 69: int sc_ntargets;
! 70: struct sdmmc_ccb *sc_ccbs; /* allocated ccbs */
! 71: struct sdmmc_ccb_list sc_ccb_freeq; /* free ccbs */
! 72: struct sdmmc_ccb_list sc_ccb_runq; /* queued ccbs */
! 73: };
! 74:
! 75: int sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *, int);
! 76: void sdmmc_free_ccbs(struct sdmmc_scsi_softc *);
! 77: struct sdmmc_ccb *sdmmc_get_ccb(struct sdmmc_scsi_softc *, int);
! 78: void sdmmc_put_ccb(struct sdmmc_ccb *);
! 79:
! 80: int sdmmc_scsi_cmd(struct scsi_xfer *);
! 81: int sdmmc_start_xs(struct sdmmc_softc *, struct sdmmc_ccb *);
! 82: void sdmmc_complete_xs(void *);
! 83: void sdmmc_done_xs(struct sdmmc_ccb *);
! 84: void sdmmc_stimeout(void *);
! 85: void sdmmc_scsi_minphys(struct buf *);
! 86:
! 87: #define DEVNAME(sc) SDMMCDEVNAME(sc)
! 88:
! 89: #ifdef SDMMC_DEBUG
! 90: #define DPRINTF(s) printf s
! 91: #else
! 92: #define DPRINTF(s) /**/
! 93: #endif
! 94:
! 95: void
! 96: sdmmc_scsi_attach(struct sdmmc_softc *sc)
! 97: {
! 98: struct scsibus_attach_args saa;
! 99: struct sdmmc_scsi_softc *scbus;
! 100: struct sdmmc_function *sf;
! 101:
! 102: MALLOC(scbus, struct sdmmc_scsi_softc *,
! 103: sizeof *scbus, M_DEVBUF, M_WAITOK);
! 104: bzero(scbus, sizeof *scbus);
! 105:
! 106: MALLOC(scbus->sc_tgt, struct sdmmc_scsi_target *,
! 107: sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1),
! 108: M_DEVBUF, M_WAITOK);
! 109: bzero(scbus->sc_tgt, sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1));
! 110:
! 111: /*
! 112: * Each card that sent us a CID in the identification stage
! 113: * gets a SCSI ID > 0, whether it is a memory card or not.
! 114: */
! 115: scbus->sc_ntargets = 1;
! 116: SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
! 117: if (scbus->sc_ntargets >= SDMMC_SCSIID_MAX+1)
! 118: break;
! 119: scbus->sc_tgt[scbus->sc_ntargets].card = sf;
! 120: scbus->sc_ntargets++;
! 121: }
! 122:
! 123: /* Preallocate some CCBs and initialize the CCB lists. */
! 124: if (sdmmc_alloc_ccbs(scbus, SDMMC_SCSI_MAXCMDS) != 0) {
! 125: printf("%s: can't allocate ccbs\n", sc->sc_dev.dv_xname);
! 126: goto free_sctgt;
! 127: }
! 128:
! 129: sc->sc_scsibus = scbus;
! 130:
! 131: scbus->sc_adapter.scsi_cmd = sdmmc_scsi_cmd;
! 132: scbus->sc_adapter.scsi_minphys = sdmmc_scsi_minphys;
! 133:
! 134: scbus->sc_link.adapter_target = SDMMC_SCSIID_HOST;
! 135: scbus->sc_link.adapter_buswidth = scbus->sc_ntargets;
! 136: scbus->sc_link.adapter_softc = sc;
! 137: scbus->sc_link.luns = 1;
! 138: scbus->sc_link.openings = 1;
! 139: scbus->sc_link.adapter = &scbus->sc_adapter;
! 140:
! 141: bzero(&saa, sizeof(saa));
! 142: saa.saa_sc_link = &scbus->sc_link;
! 143:
! 144: scbus->sc_child = config_found(&sc->sc_dev, &saa, scsiprint);
! 145: if (scbus->sc_child == NULL) {
! 146: printf("%s: can't attach scsibus\n", sc->sc_dev.dv_xname);
! 147: goto free_ccbs;
! 148: }
! 149: return;
! 150:
! 151: free_ccbs:
! 152: sc->sc_scsibus = NULL;
! 153: sdmmc_free_ccbs(scbus);
! 154: free_sctgt:
! 155: free(scbus->sc_tgt, M_DEVBUF);
! 156: free(scbus, M_DEVBUF);
! 157: }
! 158:
! 159: void
! 160: sdmmc_scsi_detach(struct sdmmc_softc *sc)
! 161: {
! 162: struct sdmmc_scsi_softc *scbus;
! 163: struct sdmmc_ccb *ccb;
! 164: int s;
! 165:
! 166: scbus = sc->sc_scsibus;
! 167: if (scbus == NULL)
! 168: return;
! 169:
! 170: /* Complete all open scsi xfers. */
! 171: s = splbio();
! 172: for (ccb = TAILQ_FIRST(&scbus->sc_ccb_runq); ccb != NULL;
! 173: ccb = TAILQ_FIRST(&scbus->sc_ccb_runq))
! 174: sdmmc_stimeout(ccb);
! 175: splx(s);
! 176:
! 177: if (scbus->sc_child != NULL)
! 178: config_detach(scbus->sc_child, DETACH_FORCE);
! 179:
! 180: if (scbus->sc_tgt != NULL)
! 181: FREE(scbus->sc_tgt, M_DEVBUF);
! 182:
! 183: sdmmc_free_ccbs(scbus);
! 184: FREE(scbus, M_DEVBUF);
! 185: sc->sc_scsibus = NULL;
! 186: }
! 187:
! 188: /*
! 189: * CCB management
! 190: */
! 191:
! 192: int
! 193: sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *scbus, int nccbs)
! 194: {
! 195: struct sdmmc_ccb *ccb;
! 196: int i;
! 197:
! 198: scbus->sc_ccbs = malloc(sizeof(struct sdmmc_ccb) * nccbs,
! 199: M_DEVBUF, M_NOWAIT);
! 200: if (scbus->sc_ccbs == NULL)
! 201: return 1;
! 202:
! 203: TAILQ_INIT(&scbus->sc_ccb_freeq);
! 204: TAILQ_INIT(&scbus->sc_ccb_runq);
! 205:
! 206: for (i = 0; i < nccbs; i++) {
! 207: ccb = &scbus->sc_ccbs[i];
! 208: ccb->ccb_scbus = scbus;
! 209: ccb->ccb_state = SDMMC_CCB_FREE;
! 210: ccb->ccb_flags = 0;
! 211: ccb->ccb_xs = NULL;
! 212: ccb->ccb_done = NULL;
! 213:
! 214: TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link);
! 215: }
! 216: return 0;
! 217: }
! 218:
! 219: void
! 220: sdmmc_free_ccbs(struct sdmmc_scsi_softc *scbus)
! 221: {
! 222: if (scbus->sc_ccbs != NULL) {
! 223: free(scbus->sc_ccbs, M_DEVBUF);
! 224: scbus->sc_ccbs = NULL;
! 225: }
! 226: }
! 227:
! 228: struct sdmmc_ccb *
! 229: sdmmc_get_ccb(struct sdmmc_scsi_softc *scbus, int flags)
! 230: {
! 231: struct sdmmc_ccb *ccb;
! 232: int s;
! 233:
! 234: s = splbio();
! 235: while ((ccb = TAILQ_FIRST(&scbus->sc_ccb_freeq)) == NULL &&
! 236: !ISSET(flags, SCSI_NOSLEEP))
! 237: tsleep(&scbus->sc_ccb_freeq, PRIBIO, "getccb", 0);
! 238: if (ccb != NULL) {
! 239: TAILQ_REMOVE(&scbus->sc_ccb_freeq, ccb, ccb_link);
! 240: ccb->ccb_state = SDMMC_CCB_READY;
! 241: }
! 242: splx(s);
! 243: return ccb;
! 244: }
! 245:
! 246: void
! 247: sdmmc_put_ccb(struct sdmmc_ccb *ccb)
! 248: {
! 249: struct sdmmc_scsi_softc *scbus = ccb->ccb_scbus;
! 250: int s;
! 251:
! 252: s = splbio();
! 253: if (ccb->ccb_state == SDMMC_CCB_QUEUED)
! 254: TAILQ_REMOVE(&scbus->sc_ccb_runq, ccb, ccb_link);
! 255: ccb->ccb_state = SDMMC_CCB_FREE;
! 256: ccb->ccb_flags = 0;
! 257: ccb->ccb_xs = NULL;
! 258: ccb->ccb_done = NULL;
! 259: TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link);
! 260: if (TAILQ_NEXT(ccb, ccb_link) == NULL)
! 261: wakeup(&scbus->sc_ccb_freeq);
! 262: splx(s);
! 263: }
! 264:
! 265: /*
! 266: * SCSI command emulation
! 267: */
! 268:
! 269: /* XXX move to some sort of "scsi emulation layer". */
! 270: static void
! 271: sdmmc_scsi_decode_rw(struct scsi_xfer *xs, u_int32_t *blocknop,
! 272: u_int32_t *blockcntp)
! 273: {
! 274: struct scsi_rw *rw;
! 275: struct scsi_rw_big *rwb;
! 276:
! 277: if (xs->cmdlen == 6) {
! 278: rw = (struct scsi_rw *)xs->cmd;
! 279: *blocknop = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff);
! 280: *blockcntp = rw->length ? rw->length : 0x100;
! 281: } else {
! 282: rwb = (struct scsi_rw_big *)xs->cmd;
! 283: *blocknop = _4btol(rwb->addr);
! 284: *blockcntp = _2btol(rwb->length);
! 285: }
! 286: }
! 287:
! 288: int
! 289: sdmmc_scsi_cmd(struct scsi_xfer *xs)
! 290: {
! 291: struct scsi_link *link = xs->sc_link;
! 292: struct sdmmc_softc *sc = link->adapter_softc;
! 293: struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
! 294: struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
! 295: struct scsi_inquiry_data inq;
! 296: struct scsi_read_cap_data rcd;
! 297: u_int32_t blockno;
! 298: u_int32_t blockcnt;
! 299: struct sdmmc_ccb *ccb;
! 300: int s;
! 301:
! 302: if (link->target >= scbus->sc_ntargets || tgt->card == NULL ||
! 303: link->lun != 0) {
! 304: DPRINTF(("%s: sdmmc_scsi_cmd: no target %d\n",
! 305: DEVNAME(sc), link->target));
! 306: /* XXX should be XS_SENSE and sense filled out */
! 307: xs->error = XS_DRIVER_STUFFUP;
! 308: xs->flags |= ITSDONE;
! 309: s = splbio();
! 310: scsi_done(xs);
! 311: splx(s);
! 312: return COMPLETE;
! 313: }
! 314:
! 315: DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)\n",
! 316: DEVNAME(sc), link->target, xs->cmd->opcode, curproc ?
! 317: curproc->p_comm : "", xs->flags & SCSI_POLL));
! 318:
! 319: xs->error = XS_NOERROR;
! 320:
! 321: switch (xs->cmd->opcode) {
! 322: case READ_COMMAND:
! 323: case READ_BIG:
! 324: case WRITE_COMMAND:
! 325: case WRITE_BIG:
! 326: /* Deal with I/O outside the switch. */
! 327: break;
! 328:
! 329: case INQUIRY:
! 330: bzero(&inq, sizeof inq);
! 331: inq.device = T_DIRECT;
! 332: inq.version = 2;
! 333: inq.response_format = 2;
! 334: inq.additional_length = 32;
! 335: strlcpy(inq.vendor, "SD/MMC ", sizeof(inq.vendor));
! 336: snprintf(inq.product, sizeof(inq.product),
! 337: "Drive #%02d", link->target);
! 338: strlcpy(inq.revision, " ", sizeof(inq.revision));
! 339: bcopy(&inq, xs->data, MIN(xs->datalen, sizeof inq));
! 340: s = splbio();
! 341: scsi_done(xs);
! 342: splx(s);
! 343: return COMPLETE;
! 344:
! 345: case TEST_UNIT_READY:
! 346: case START_STOP:
! 347: case SYNCHRONIZE_CACHE:
! 348: return COMPLETE;
! 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: s = splbio();
! 356: scsi_done(xs);
! 357: splx(s);
! 358: return COMPLETE;
! 359:
! 360: default:
! 361: DPRINTF(("%s: unsupported scsi command %#x\n",
! 362: DEVNAME(sc), xs->cmd->opcode));
! 363: xs->error = XS_DRIVER_STUFFUP;
! 364: s = splbio();
! 365: scsi_done(xs);
! 366: splx(s);
! 367: return COMPLETE;
! 368: }
! 369:
! 370: /* A read or write operation. */
! 371: sdmmc_scsi_decode_rw(xs, &blockno, &blockcnt);
! 372:
! 373: if (blockno >= tgt->card->csd.capacity ||
! 374: blockno + blockcnt > tgt->card->csd.capacity) {
! 375: DPRINTF(("%s: out of bounds %u-%u >= %u\n", DEVNAME(sc),
! 376: blockno, blockcnt, tgt->card->csd.capacity));
! 377: xs->error = XS_DRIVER_STUFFUP;
! 378: s = splbio();
! 379: scsi_done(xs);
! 380: splx(s);
! 381: return COMPLETE;
! 382: }
! 383:
! 384: ccb = sdmmc_get_ccb(sc->sc_scsibus, xs->flags);
! 385: if (ccb == NULL) {
! 386: printf("%s: out of ccbs\n", DEVNAME(sc));
! 387: xs->error = XS_DRIVER_STUFFUP;
! 388: s = splbio();
! 389: scsi_done(xs);
! 390: splx(s);
! 391: return COMPLETE;
! 392: }
! 393:
! 394: ccb->ccb_xs = xs;
! 395: ccb->ccb_done = sdmmc_done_xs;
! 396:
! 397: ccb->ccb_blockcnt = blockcnt;
! 398: ccb->ccb_blockno = blockno;
! 399:
! 400: return sdmmc_start_xs(sc, ccb);
! 401: }
! 402:
! 403: int
! 404: sdmmc_start_xs(struct sdmmc_softc *sc, struct sdmmc_ccb *ccb)
! 405: {
! 406: struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
! 407: struct scsi_xfer *xs = ccb->ccb_xs;
! 408: int s;
! 409:
! 410: timeout_set(&xs->stimeout, sdmmc_stimeout, ccb);
! 411: sdmmc_init_task(&ccb->ccb_task, sdmmc_complete_xs, ccb);
! 412:
! 413: s = splbio();
! 414: TAILQ_INSERT_TAIL(&scbus->sc_ccb_runq, ccb, ccb_link);
! 415: ccb->ccb_state = SDMMC_CCB_QUEUED;
! 416: splx(s);
! 417:
! 418: if (ISSET(xs->flags, SCSI_POLL)) {
! 419: sdmmc_complete_xs(ccb);
! 420: return COMPLETE;
! 421: }
! 422:
! 423: timeout_add(&xs->stimeout, (xs->timeout * hz) / 1000);
! 424: sdmmc_add_task(sc, &ccb->ccb_task);
! 425: return SUCCESSFULLY_QUEUED;
! 426: }
! 427:
! 428: void
! 429: sdmmc_complete_xs(void *arg)
! 430: {
! 431: struct sdmmc_ccb *ccb = arg;
! 432: struct scsi_xfer *xs = ccb->ccb_xs;
! 433: struct scsi_link *link = xs->sc_link;
! 434: struct sdmmc_softc *sc = link->adapter_softc;
! 435: struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
! 436: struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
! 437: int error;
! 438: int s;
! 439:
! 440: DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)"
! 441: " complete\n", DEVNAME(sc), link->target, xs->cmd->opcode,
! 442: curproc ? curproc->p_comm : "", xs->flags & SCSI_POLL));
! 443:
! 444: s = splbio();
! 445:
! 446: if (ISSET(xs->flags, SCSI_DATA_IN))
! 447: error = sdmmc_mem_read_block(tgt->card, ccb->ccb_blockno,
! 448: xs->data, ccb->ccb_blockcnt * DEV_BSIZE);
! 449: else
! 450: error = sdmmc_mem_write_block(tgt->card, ccb->ccb_blockno,
! 451: xs->data, ccb->ccb_blockcnt * DEV_BSIZE);
! 452:
! 453: if (error != 0)
! 454: xs->error = XS_DRIVER_STUFFUP;
! 455:
! 456: ccb->ccb_done(ccb);
! 457: splx(s);
! 458: }
! 459:
! 460: void
! 461: sdmmc_done_xs(struct sdmmc_ccb *ccb)
! 462: {
! 463: struct scsi_xfer *xs = ccb->ccb_xs;
! 464: #ifdef SDMMC_DEBUG
! 465: struct scsi_link *link = xs->sc_link;
! 466: struct sdmmc_softc *sc = link->adapter_softc;
! 467: #endif
! 468:
! 469: timeout_del(&xs->stimeout);
! 470:
! 471: DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (error=%#x)"
! 472: " done\n", DEVNAME(sc), link->target, xs->cmd->opcode,
! 473: curproc ? curproc->p_comm : "", xs->error));
! 474:
! 475: xs->resid = 0;
! 476: xs->flags |= ITSDONE;
! 477:
! 478: if (ISSET(ccb->ccb_flags, SDMMC_CCB_F_ERR))
! 479: xs->error = XS_DRIVER_STUFFUP;
! 480:
! 481: sdmmc_put_ccb(ccb);
! 482: scsi_done(xs);
! 483: }
! 484:
! 485: void
! 486: sdmmc_stimeout(void *arg)
! 487: {
! 488: struct sdmmc_ccb *ccb = arg;
! 489: int s;
! 490:
! 491: s = splbio();
! 492: ccb->ccb_flags |= SDMMC_CCB_F_ERR;
! 493: if (sdmmc_task_pending(&ccb->ccb_task)) {
! 494: sdmmc_del_task(&ccb->ccb_task);
! 495: ccb->ccb_done(ccb);
! 496: }
! 497: splx(s);
! 498: }
! 499:
! 500: void
! 501: sdmmc_scsi_minphys(struct buf *bp)
! 502: {
! 503: /* XXX limit to max. transfer size supported by card/host? */
! 504: if (bp->b_bcount > DEV_BSIZE)
! 505: bp->b_bcount = DEV_BSIZE;
! 506: minphys(bp);
! 507: }
CVSweb