|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <cpio.h> | 
|  | #include <error.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <net/ip.h> | 
|  | #include <pmap.h> | 
|  | #include <sd.h> | 
|  | #include <slab.h> | 
|  | #include <smp.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  |  | 
|  | static int scsitest(struct sdreq *r) | 
|  | { | 
|  | r->write = 0; | 
|  | memset(r->cmd, 0, sizeof(r->cmd)); | 
|  | r->cmd[1] = r->lun << 5; | 
|  | r->clen = 6; | 
|  | r->data = NULL; | 
|  | r->dlen = 0; | 
|  | r->flags = 0; | 
|  |  | 
|  | r->status = ~0; | 
|  |  | 
|  | return r->unit->dev->ifc->rio(r); | 
|  | } | 
|  |  | 
|  | int scsiverify(struct sdunit *unit) | 
|  | { | 
|  | struct sdreq *r; | 
|  | int i, status; | 
|  | uint8_t *inquiry; | 
|  |  | 
|  | r = kzmalloc(sizeof(struct sdreq), 0); | 
|  | if (r == NULL) | 
|  | return 0; | 
|  | inquiry = kzmalloc(sizeof(unit->inquiry), MEM_WAIT); | 
|  | if (inquiry == NULL) { | 
|  | kfree(r); | 
|  | return 0; | 
|  | } | 
|  | r->unit = unit; | 
|  | r->lun = 0; /* ??? */ | 
|  |  | 
|  | memset(unit->inquiry, 0, sizeof(unit->inquiry)); | 
|  | r->write = 0; | 
|  | r->cmd[0] = 0x12; | 
|  | r->cmd[1] = r->lun << 5; | 
|  | r->cmd[4] = sizeof(unit->inquiry) - 1; | 
|  | r->clen = 6; | 
|  | r->data = inquiry; | 
|  | r->dlen = sizeof(unit->inquiry) - 1; | 
|  | r->flags = 0; | 
|  |  | 
|  | r->status = ~0; | 
|  | if (unit->dev->ifc->rio(r) != SDok) { | 
|  | kfree(r); | 
|  | return 0; | 
|  | } | 
|  | memmove(unit->inquiry, inquiry, r->dlen); | 
|  | kfree(inquiry); | 
|  |  | 
|  | for (i = 0; i < 3; i++) { | 
|  | while ((status = scsitest(r)) == SDbusy) | 
|  | ; | 
|  | if (status == SDok || status != SDcheck) | 
|  | break; | 
|  | if (!(r->flags & SDvalidsense)) | 
|  | break; | 
|  | if ((r->sense[2] & 0x0F) != 0x02) | 
|  | continue; | 
|  |  | 
|  | /* | 
|  | * Unit is 'not ready'. | 
|  | * If it is in the process of becoming ready or needs | 
|  | * an initialising command, set status so it will be spun-up | 
|  | * below. | 
|  | * If there's no medium, that's OK too, but don't | 
|  | * try to spin it up. | 
|  | */ | 
|  | if (r->sense[12] == 0x04) { | 
|  | if (r->sense[13] == 0x02 || r->sense[13] == 0x01) { | 
|  | status = SDok; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (r->sense[12] == 0x3A) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (status == SDok) { | 
|  | /* | 
|  | * Try to ensure a direct-access device is spinning. | 
|  | * Don't wait for completion, ignore the result. | 
|  | */ | 
|  | if ((unit->inquiry[0] & SDinq0periphtype) == SDperdisk) { | 
|  | memset(r->cmd, 0, sizeof(r->cmd)); | 
|  | r->write = 0; | 
|  | r->cmd[0] = 0x1B; | 
|  | r->cmd[1] = (r->lun << 5) | 0x01; | 
|  | r->cmd[4] = 1; | 
|  | r->clen = 6; | 
|  | r->data = NULL; | 
|  | r->dlen = 0; | 
|  | r->flags = 0; | 
|  |  | 
|  | r->status = ~0; | 
|  | unit->dev->ifc->rio(r); | 
|  | } | 
|  | } | 
|  | kfree(r); | 
|  |  | 
|  | if (status == SDok || status == SDcheck) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int scsirio(struct sdreq *r) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | /* | 
|  | * Perform an I/O request, returning | 
|  | *	-1	failure | 
|  | *	 0	ok | 
|  | *	 1	no medium present | 
|  | *	 2	retry | 
|  | * The contents of r may be altered so the | 
|  | * caller should re-initialise if necesary. | 
|  | */ | 
|  | r->status = ~0; | 
|  | switch (r->unit->dev->ifc->rio(r)) { | 
|  | default: | 
|  | break; | 
|  | case SDcheck: | 
|  | if (!(r->flags & SDvalidsense)) | 
|  | break; | 
|  | switch (r->sense[2] & 0x0F) { | 
|  | case 0x00: /* no sense */ | 
|  | case 0x01: /* recovered error */ | 
|  | return 2; | 
|  | case 0x06: /* check condition */ | 
|  | /* | 
|  | * 0x28 - not ready to ready transition, | 
|  | *	  medium may have changed. | 
|  | * 0x29 - power on or some type of reset. | 
|  | */ | 
|  | if (r->sense[12] == 0x28 && r->sense[13] == 0) | 
|  | return 2; | 
|  | if (r->sense[12] == 0x29) | 
|  | return 2; | 
|  | break; | 
|  | case 0x02: /* not ready */ | 
|  | /* | 
|  | * If no medium present, bail out. | 
|  | * If unit is becoming ready, rather than not | 
|  | * not ready, wait a little then poke it again. | 
|  | */ | 
|  | if (r->sense[12] == 0x3A) | 
|  | break; | 
|  | if (r->sense[12] != 0x04 || r->sense[13] != 0x01) | 
|  | break; | 
|  |  | 
|  | while (waserror()) | 
|  | ; | 
|  | kthread_usleep(500 * 1000); | 
|  | poperror(); | 
|  | scsitest(r); | 
|  | return 2; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case SDok: | 
|  | return 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int scsionline(struct sdunit *unit) | 
|  | { | 
|  | struct sdreq *r; | 
|  | uint8_t *p; | 
|  | int ok, retries; | 
|  |  | 
|  | r = kzmalloc(sizeof(struct sdreq), 0); | 
|  | if (r == NULL) | 
|  | return 0; | 
|  | p = kzmalloc(8, 0); | 
|  | if (p == NULL) { | 
|  | kfree(r); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | ok = 0; | 
|  |  | 
|  | r->unit = unit; | 
|  | r->lun = 0; /* ??? */ | 
|  | for (retries = 0; retries < 10; retries++) { | 
|  | /* | 
|  | * Read-capacity is mandatory for DA, WORM, CD-ROM and | 
|  | * MO. It may return 'not ready' if type DA is not | 
|  | * spun up, type MO or type CD-ROM are not loaded or just | 
|  | * plain slow getting their act together after a reset. | 
|  | */ | 
|  | r->write = 0; | 
|  | memset(r->cmd, 0, sizeof(r->cmd)); | 
|  | r->cmd[0] = 0x25; | 
|  | r->cmd[1] = r->lun << 5; | 
|  | r->clen = 10; | 
|  | r->data = p; | 
|  | r->dlen = 8; | 
|  | r->flags = 0; | 
|  |  | 
|  | r->status = ~0; | 
|  | switch (scsirio(r)) { | 
|  | default: | 
|  | break; | 
|  | case 0: | 
|  | unit->sectors = | 
|  | (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; | 
|  | unit->secsize = | 
|  | (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; | 
|  |  | 
|  | /* | 
|  | * Some ATAPI CD readers lie about the block size. | 
|  | * Since we don't read audio via this interface | 
|  | * it's okay to always fudge this. | 
|  | */ | 
|  | if (unit->secsize == 2352) | 
|  | unit->secsize = 2048; | 
|  | /* | 
|  | * Devices with removable media may return 0 sectors | 
|  | * when they have empty media (e.g. sata dvd writers); | 
|  | * if so, keep the count zero. | 
|  | * | 
|  | * Read-capacity returns the LBA of the last sector, | 
|  | * therefore the number of sectors must be incremented. | 
|  | */ | 
|  | if (unit->sectors != 0) | 
|  | unit->sectors++; | 
|  | ok = 1; | 
|  | break; | 
|  | case 1: | 
|  | ok = 1; | 
|  | break; | 
|  | case 2: | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | } | 
|  | kfree(p); | 
|  | kfree(r); | 
|  |  | 
|  | if (ok) | 
|  | return ok + retries; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int scsiexec(struct sdunit *unit, int write, uint8_t *cmd, int clen, void *data, | 
|  | int *dlen) | 
|  | { | 
|  | struct sdreq *r; | 
|  | int status; | 
|  |  | 
|  | r = kzmalloc(sizeof(struct sdreq), 0); | 
|  | if (r == NULL) | 
|  | return SDmalloc; | 
|  | r->unit = unit; | 
|  | r->lun = cmd[1] >> 5; /* ??? */ | 
|  | r->write = write; | 
|  | memmove(r->cmd, cmd, clen); | 
|  | r->clen = clen; | 
|  | r->data = data; | 
|  | if (dlen) | 
|  | r->dlen = *dlen; | 
|  | r->flags = 0; | 
|  |  | 
|  | r->status = ~0; | 
|  |  | 
|  | /* | 
|  | * Call the device-specific I/O routine. | 
|  | * There should be no calls to 'error()' below this | 
|  | * which percolate back up. | 
|  | */ | 
|  | switch (status = unit->dev->ifc->rio(r)) { | 
|  | case SDok: | 
|  | if (dlen) | 
|  | *dlen = r->rlen; | 
|  | /*FALLTHROUGH*/ | 
|  | case SDcheck: | 
|  | /*FALLTHROUGH*/ | 
|  | default: | 
|  | /* | 
|  | * It's more complicated than this. There are conditions | 
|  | * which are 'ok' but for which the returned status code | 
|  | * is not 'SDok'. | 
|  | * Also, not all conditions require a reqsense, might | 
|  | * need to do a reqsense here and make it available to the | 
|  | * caller somehow. | 
|  | * | 
|  | * MaƱana. | 
|  | */ | 
|  | break; | 
|  | } | 
|  | kfree(r); | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | static void scsifmt10(struct sdreq *r, int write, int lun, uint32_t nb, | 
|  | uint64_t bno) | 
|  | { | 
|  | uint8_t *c; | 
|  |  | 
|  | c = r->cmd; | 
|  | if (write == 0) | 
|  | c[0] = 0x28; | 
|  | else | 
|  | c[0] = 0x2A; | 
|  | c[1] = lun << 5; | 
|  | c[2] = bno >> 24; | 
|  | c[3] = bno >> 16; | 
|  | c[4] = bno >> 8; | 
|  | c[5] = bno; | 
|  | c[6] = 0; | 
|  | c[7] = nb >> 8; | 
|  | c[8] = nb; | 
|  | c[9] = 0; | 
|  |  | 
|  | r->clen = 10; | 
|  | } | 
|  |  | 
|  | static void scsifmt16(struct sdreq *r, int write, int lun, uint32_t nb, | 
|  | uint64_t bno) | 
|  | { | 
|  | uint8_t *c; | 
|  |  | 
|  | c = r->cmd; | 
|  | if (write == 0) | 
|  | c[0] = 0x88; | 
|  | else | 
|  | c[0] = 0x8A; | 
|  | c[1] = lun << 5; /* so wrong */ | 
|  | c[2] = bno >> 56; | 
|  | c[3] = bno >> 48; | 
|  | c[4] = bno >> 40; | 
|  | c[5] = bno >> 32; | 
|  | c[6] = bno >> 24; | 
|  | c[7] = bno >> 16; | 
|  | c[8] = bno >> 8; | 
|  | c[9] = bno; | 
|  | c[10] = nb >> 24; | 
|  | c[11] = nb >> 16; | 
|  | c[12] = nb >> 8; | 
|  | c[13] = nb; | 
|  | c[14] = 0; | 
|  | c[15] = 0; | 
|  |  | 
|  | r->clen = 16; | 
|  | } | 
|  |  | 
|  | int32_t scsibio(struct sdunit *unit, int lun, int write, void *data, int32_t nb, | 
|  | uint64_t bno) | 
|  | { | 
|  | struct sdreq *r; | 
|  | int32_t rlen; | 
|  |  | 
|  | r = kzmalloc(sizeof(struct sdreq), 0); | 
|  | if (r == NULL) | 
|  | error(ENOMEM, "scsibio: can't allocate %d bytes", sizeof(*r)); | 
|  | r->unit = unit; | 
|  | r->lun = lun; | 
|  | again: | 
|  | r->write = write; | 
|  | if (bno >= (1ULL << 32)) | 
|  | scsifmt16(r, write, lun, nb, bno); | 
|  | else | 
|  | scsifmt10(r, write, lun, nb, bno); | 
|  | r->data = data; | 
|  | r->dlen = nb * unit->secsize; | 
|  | r->flags = 0; | 
|  |  | 
|  | r->status = ~0; | 
|  | switch (scsirio(r)) { | 
|  | default: | 
|  | rlen = -1; | 
|  | break; | 
|  | case 0: | 
|  | rlen = r->rlen; | 
|  | break; | 
|  | case 2: | 
|  | rlen = -1; | 
|  | if (!(r->flags & SDvalidsense)) | 
|  | break; | 
|  | switch (r->sense[2] & 0x0F) { | 
|  | default: | 
|  | break; | 
|  | case 0x01: /* recovered error */ | 
|  | printd("%s: recovered error at sector %llu\n", | 
|  | unit->SDperm.name, bno); | 
|  | rlen = r->rlen; | 
|  | break; | 
|  | case 0x06: /* check condition */ | 
|  | /* | 
|  | * Check for a removeable media change. | 
|  | * If so, mark it by zapping the geometry info | 
|  | * to force an online request. | 
|  | */ | 
|  | if (r->sense[12] != 0x28 || r->sense[13] != 0) | 
|  | break; | 
|  | if (unit->inquiry[1] & SDinq1removable) | 
|  | unit->sectors = 0; | 
|  | break; | 
|  | case 0x02: /* not ready */ | 
|  | /* | 
|  | * If unit is becoming ready, | 
|  | * rather than not not ready, try again. | 
|  | */ | 
|  | if (r->sense[12] == 0x04 && r->sense[13] == 0x01) | 
|  | goto again; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  | kfree(r); | 
|  |  | 
|  | return rlen; | 
|  | } |