123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- /*
- * 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 <u.h>
- #include <libc.h>
- /*
- * BUGS:
- * no luns
- * and incomplete in many other ways
- */
- #include <disk.h>
- #include "scsireq.h"
- enum {
- Debug = 0,
- };
- /*
- * exabyte tape drives, at least old ones like the 8200 and 8505,
- * are dumb: you have to read the exact block size on the tape,
- * they don't take 10-byte SCSI commands, and various other fine points.
- */
- extern int exabyte, force6bytecmds;
- static int debug = Debug;
- int32_t
- SRready(ScsiReq *rp)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = cmd;
- rp->data.count = 0;
- rp->data.write = 1;
- return SRrequest(rp);
- }
- int32_t
- SRrewind(ScsiReq *rp)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdRewind;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = cmd;
- rp->data.count = 0;
- rp->data.write = 1;
- if(SRrequest(rp) >= 0){
- rp->offset = 0;
- return 0;
- }
- return -1;
- }
- int32_t
- SRreqsense(ScsiReq *rp)
- {
- uint8_t cmd[6];
- ScsiReq req;
- int32_t status;
- if(rp->status == Status_SD){
- rp->status = STok;
- return 0;
- }
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdRsense;
- cmd[4] = sizeof(req.sense);
- memset(&req, 0, sizeof(req));
- if(rp->flags&Fusb)
- req.flags |= Fusb;
- req.fd = rp->fd;
- req.umsc = rp->umsc;
- req.cmd.p = cmd;
- req.cmd.count = sizeof cmd;
- req.data.p = rp->sense;
- req.data.count = sizeof(rp->sense);
- req.data.write = 0;
- status = SRrequest(&req);
- rp->status = req.status;
- return status;
- }
- int32_t
- SRformat(ScsiReq *rp)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdFormat;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = cmd;
- rp->data.count = 6;
- rp->data.write = 0;
- return SRrequest(rp);
- }
- int32_t
- SRrblimits(ScsiReq *rp, uint8_t *list)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdRblimits;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = list;
- rp->data.count = 6;
- rp->data.write = 0;
- return SRrequest(rp);
- }
- static int
- dirdevrw(ScsiReq *rp, uint8_t *cmd, int32_t nbytes)
- {
- int32_t n;
- n = nbytes / rp->lbsize;
- if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){
- PUTBE24(cmd+1, rp->offset);
- cmd[4] = n;
- cmd[5] = 0;
- return 6;
- }
- cmd[0] |= ScmdExtread;
- cmd[1] = 0;
- PUTBELONG(cmd+2, rp->offset);
- cmd[6] = 0;
- cmd[7] = n>>8;
- cmd[8] = n;
- cmd[9] = 0;
- return 10;
- }
- static int
- seqdevrw(ScsiReq *rp, uint8_t *cmd, int32_t nbytes)
- {
- int32_t n;
- /* don't set Cmd1sili; we want the ILI bit instead of a fatal error */
- cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0;
- n = nbytes / rp->lbsize;
- PUTBE24(cmd+2, n);
- cmd[5] = 0;
- return 6;
- }
- int32_t
- SRread(ScsiReq *rp, void *buf, int32_t nbytes)
- {
- uint8_t cmd[10];
- int32_t n;
- if((nbytes % rp->lbsize) || nbytes > maxiosize){
- if(debug){
- if (nbytes % rp->lbsize)
- fprint(2, "scuzz: i/o size %ld %% %ld != 0\n",
- nbytes, rp->lbsize);
- else
- fprint(2, "scuzz: i/o size %ld > %ld\n",
- nbytes, maxiosize);
- }
- rp->status = Status_BADARG;
- return -1;
- }
- /* set up scsi read cmd */
- cmd[0] = ScmdRead;
- if(rp->flags & Fseqdev)
- rp->cmd.count = seqdevrw(rp, cmd, nbytes);
- else
- rp->cmd.count = dirdevrw(rp, cmd, nbytes);
- rp->cmd.p = cmd;
- rp->data.p = buf;
- rp->data.count = nbytes;
- rp->data.write = 0;
- /* issue it */
- n = SRrequest(rp);
- if(n != -1){ /* it worked? */
- rp->offset += n / rp->lbsize;
- return n;
- }
- /* request failed; maybe we just read a short record? */
- if (exabyte) {
- fprint(2, "read error\n");
- rp->status = STcheck;
- return n;
- }
- if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid))
- return -1;
- /* compute # of bytes not read */
- n = GETBELONG(rp->sense+3) * rp->lbsize;
- if (debug)
- fprint(2,
- "SRread: request failed with sense data; sense byte count %ld\n",
- n);
- if(!(rp->flags & Fseqdev))
- return -1;
- /* device is a tape or something similar */
- if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 ||
- (rp->sense[2] & Sd2ili && n > 0))
- rp->data.count = nbytes - n;
- else
- return -1;
- n = rp->data.count;
- if (!rp->readblock++ || debug)
- fprint(2, "SRread: tape data count %ld%s\n", n,
- (rp->sense[2] & Sd2ili? " with ILI": ""));
- rp->status = STok;
- rp->offset += n / rp->lbsize;
- return n;
- }
- int32_t
- SRwrite(ScsiReq *rp, void *buf, int32_t nbytes)
- {
- uint8_t cmd[10];
- int32_t n;
- if((nbytes % rp->lbsize) || nbytes > maxiosize){
- if(debug){
- if (nbytes % rp->lbsize)
- fprint(2, "scuzz: i/o size %ld %% %ld != 0\n",
- nbytes, rp->lbsize);
- else
- fprint(2, "scuzz: i/o size %ld > %ld\n",
- nbytes, maxiosize);
- }
- rp->status = Status_BADARG;
- return -1;
- }
- /* set up scsi write cmd */
- cmd[0] = ScmdWrite;
- if(rp->flags & Fseqdev)
- rp->cmd.count = seqdevrw(rp, cmd, nbytes);
- else
- rp->cmd.count = dirdevrw(rp, cmd, nbytes);
- rp->cmd.p = cmd;
- rp->data.p = buf;
- rp->data.count = nbytes;
- rp->data.write = 1;
- /* issue it */
- if((n = SRrequest(rp)) == -1){
- if (exabyte) {
- fprint(2, "write error\n");
- rp->status = STcheck;
- return n;
- }
- if(rp->status != Status_SD || rp->sense[2] != Sd2eom)
- return -1;
- if(rp->sense[0] & Sd0valid){
- n -= GETBELONG(rp->sense+3) * rp->lbsize;
- rp->data.count = nbytes - n;
- }
- else
- rp->data.count = nbytes;
- n = rp->data.count;
- }
- rp->offset += n / rp->lbsize;
- return n;
- }
- int32_t
- SRseek(ScsiReq *rp, int32_t offset, int type)
- {
- uint8_t cmd[10];
- switch(type){
- case 0:
- break;
- case 1:
- offset += rp->offset;
- if(offset >= 0)
- break;
- /*FALLTHROUGH*/
- default:
- if(debug)
- fprint(2, "scuzz: seek failed\n");
- rp->status = Status_BADARG;
- return -1;
- }
- memset(cmd, 0, sizeof cmd);
- if(offset <= Max24off && (rp->flags & Frw10) == 0){
- cmd[0] = ScmdSeek;
- PUTBE24(cmd+1, offset & Max24off);
- rp->cmd.count = 6;
- }else{
- cmd[0] = ScmdExtseek;
- PUTBELONG(cmd+2, offset);
- rp->cmd.count = 10;
- }
- rp->cmd.p = cmd;
- rp->data.p = cmd;
- rp->data.count = 0;
- rp->data.write = 1;
- SRrequest(rp);
- if(rp->status == STok)
- return rp->offset = offset;
- return -1;
- }
- int32_t
- SRfilemark(ScsiReq *rp, uint32_t howmany)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdFmark;
- PUTBE24(cmd+2, howmany);
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = cmd;
- rp->data.count = 0;
- rp->data.write = 1;
- return SRrequest(rp);
- }
- int32_t
- SRspace(ScsiReq *rp, uint8_t code, int32_t howmany)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdSpace;
- cmd[1] = code;
- PUTBE24(cmd+2, howmany);
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = cmd;
- rp->data.count = 0;
- rp->data.write = 1;
- /*
- * what about rp->offset?
- */
- return SRrequest(rp);
- }
- int32_t
- SRinquiry(ScsiReq *rp)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdInq;
- cmd[4] = sizeof rp->inquiry;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- memset(rp->inquiry, 0, sizeof rp->inquiry);
- rp->data.p = rp->inquiry;
- rp->data.count = sizeof rp->inquiry;
- rp->data.write = 0;
- if(SRrequest(rp) >= 0){
- rp->flags |= Finqok;
- return 0;
- }
- rp->flags &= ~Finqok;
- return -1;
- }
- int32_t
- SRmodeselect6(ScsiReq *rp, uint8_t *list, int32_t nbytes)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdMselect6;
- if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
- cmd[1] = 0x10;
- cmd[4] = nbytes;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = list;
- rp->data.count = nbytes;
- rp->data.write = 1;
- return SRrequest(rp);
- }
- int32_t
- SRmodeselect10(ScsiReq *rp, uint8_t *list, int32_t nbytes)
- {
- uint8_t cmd[10];
- memset(cmd, 0, sizeof cmd);
- if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
- cmd[1] = 0x10;
- cmd[0] = ScmdMselect10;
- cmd[7] = nbytes>>8;
- cmd[8] = nbytes;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = list;
- rp->data.count = nbytes;
- rp->data.write = 1;
- return SRrequest(rp);
- }
- int32_t
- SRmodesense6(ScsiReq *rp, uint8_t page, uint8_t *list, int32_t nbytes)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdMsense6;
- cmd[2] = page;
- cmd[4] = nbytes;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = list;
- rp->data.count = nbytes;
- rp->data.write = 0;
- return SRrequest(rp);
- }
- int32_t
- SRmodesense10(ScsiReq *rp, uint8_t page, uint8_t *list, int32_t nbytes)
- {
- uint8_t cmd[10];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdMsense10;
- cmd[2] = page;
- cmd[7] = nbytes>>8;
- cmd[8] = nbytes;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = list;
- rp->data.count = nbytes;
- rp->data.write = 0;
- return SRrequest(rp);
- }
- int32_t
- SRstart(ScsiReq *rp, uint8_t code)
- {
- uint8_t cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdStart;
- cmd[4] = code;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = cmd;
- rp->data.count = 0;
- rp->data.write = 1;
- return SRrequest(rp);
- }
- int32_t
- SRrcapacity(ScsiReq *rp, uint8_t *data)
- {
- uint8_t cmd[10];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = ScmdRcapacity;
- rp->cmd.p = cmd;
- rp->cmd.count = sizeof cmd;
- rp->data.p = data;
- rp->data.count = 8;
- rp->data.write = 0;
- return SRrequest(rp);
- }
- static int32_t
- request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
- {
- int32_t n, r;
- char buf[16];
- /* this was an experiment but it seems to be a good idea */
- *status = STok;
- /* send SCSI command */
- if(write(fd, cmd->p, cmd->count) != cmd->count){
- fprint(2, "scsireq: write cmd: %r\n");
- *status = Status_SW;
- return -1;
- }
- /* read or write actual data */
- werrstr("");
- if(data->write)
- n = write(fd, data->p, data->count);
- else {
- n = read(fd, data->p, data->count);
- if (n < 0)
- memset(data->p, 0, data->count);
- else if (n < data->count)
- memset(data->p + n, 0, data->count - n);
- }
- if (n != data->count && n <= 0) {
- if (debug)
- fprint(2,
- "request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n",
- (data->write? "write": "read"),
- data->count, cmd->p[0]);
- } else if (n != data->count && (data->write || debug))
- fprint(2, "request: %s %ld of %ld bytes of actual data\n",
- (data->write? "wrote": "read"), n, data->count);
- /* read status */
- buf[0] = '\0';
- r = read(fd, buf, sizeof buf-1);
- if((exabyte && r <= 0) || (!exabyte && r < 0)){
- fprint(2, "scsireq: read status: %r\n");
- *status = Status_SW;
- return -1;
- }
- if (r >= 0)
- buf[r] = '\0';
- *status = atoi(buf);
- if(n < 0 && (exabyte || *status != STcheck))
- fprint(2, "scsireq: status 0x%2.2X: data transfer: %r\n",
- *status);
- return n;
- }
- int32_t
- SRrequest(ScsiReq *rp)
- {
- int32_t n;
- int status;
- retry:
- if(rp->flags&Fusb)
- n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status);
- else
- n = request(rp->fd, &rp->cmd, &rp->data, &status);
- switch(rp->status = status){
- case STok:
- rp->data.count = n;
- break;
- case STcheck:
- if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1)
- rp->status = Status_SD;
- if (exabyte)
- fprint(2, "SRrequest: STcheck, returning -1\n");
- return -1;
- case STbusy:
- sleep(1000);
- goto retry;
- default:
- fprint(2, "status 0x%2.2X\n", status);
- return -1;
- }
- return n;
- }
- int
- SRclose(ScsiReq *rp)
- {
- if((rp->flags & Fopen) == 0){
- if(debug)
- fprint(2, "scuzz: closing closed file\n");
- rp->status = Status_BADARG;
- return -1;
- }
- close(rp->fd);
- rp->flags = 0;
- return 0;
- }
- uint
- mkascq(ScsiReq *r)
- {
- uint8_t *u;
- u = r->sense;
- return u[2]<<16 | u[12]<<8 | u[13];
- }
- static int
- dirdevopen(ScsiReq *rp)
- {
- uint32_t blocks;
- uint8_t data[8];
- if(SRstart(rp, 1) == -1)
- /*
- * it's okay for removable media to say
- * "check condition: medium not present".
- * 3a is "medium not present".
- */
- return rp->inquiry[1] & 0x80 && (mkascq(rp) >> 8) == 0x023a?
- 0: -1;
- memset(data, 0, sizeof data);
- if(SRrcapacity(rp, data) == -1)
- return -1;
- rp->lbsize = GETBELONG(data+4);
- blocks = GETBELONG(data);
- if(debug)
- fprint(2, "scuzz: dirdevopen: logical block size %lu, "
- "# blocks %lu\n", rp->lbsize, blocks);
- /* some newer dev's don't support 6-byte commands */
- if(blocks > Max24off && !force6bytecmds)
- rp->flags |= Frw10;
- return 0;
- }
- static int
- seqdevopen(ScsiReq *rp)
- {
- uint8_t mode[16], limits[6];
- if(SRrblimits(rp, limits) == -1)
- return -1;
- if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){
- rp->flags |= Fbfixed;
- rp->lbsize = limits[4]<<8 | limits[5];
- if(debug)
- fprint(2, "scuzz: seqdevopen: logical block size %lu\n",
- rp->lbsize);
- return 0;
- }
- /*
- * On some older hardware the optional 10-byte
- * modeselect command isn't implemented.
- */
- if (force6bytecmds)
- rp->flags |= Fmode6;
- if(!(rp->flags & Fmode6)){
- /* try 10-byte command first */
- memset(mode, 0, sizeof mode);
- mode[3] = 0x10; /* device-specific param. */
- mode[7] = 8; /* block descriptor length */
- /*
- * exabytes can't handle this, and
- * modeselect(10) is optional.
- */
- if(SRmodeselect10(rp, mode, sizeof mode) != -1){
- rp->lbsize = 1;
- return 0; /* success */
- }
- /* can't do 10-byte commands, back off to 6-byte ones */
- rp->flags |= Fmode6;
- }
- /* 6-byte command */
- memset(mode, 0, sizeof mode);
- mode[2] = 0x10; /* device-specific param. */
- mode[3] = 8; /* block descriptor length */
- /*
- * bsd sez exabytes need this bit (NBE: no busy enable) in
- * vendor-specific page (0), but so far we haven't needed it.
- mode[12] |= 8;
- */
- if(SRmodeselect6(rp, mode, 4+8) == -1)
- return -1;
- rp->lbsize = 1;
- return 0;
- }
- static int
- wormdevopen(ScsiReq *rp)
- {
- int32_t status;
- uint8_t list[MaxDirData];
- if (SRstart(rp, 1) == -1 ||
- (status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1)
- return -1;
- /* nbytes = list[0]<<8 | list[1]; */
- /* # of bytes of block descriptors of 8 bytes each; not even 1? */
- if((list[6]<<8 | list[7]) < 8)
- rp->lbsize = 2048;
- else
- /* last 3 bytes of block 0 descriptor */
- rp->lbsize = GETBE24(list+13);
- if(debug)
- fprint(2, "scuzz: wormdevopen: logical block size %lu\n",
- rp->lbsize);
- return status;
- }
- int
- SRopenraw(ScsiReq *rp, char *unit)
- {
- char name[128];
- if(rp->flags & Fopen){
- if(debug)
- fprint(2, "scuzz: opening open file\n");
- rp->status = Status_BADARG;
- return -1;
- }
- memset(rp, 0, sizeof *rp);
- rp->unit = unit;
- sprint(name, "%s/raw", unit);
- if((rp->fd = open(name, ORDWR)) == -1){
- rp->status = STtimeout;
- return -1;
- }
- rp->flags = Fopen;
- return 0;
- }
- int
- SRopen(ScsiReq *rp, char *unit)
- {
- if(SRopenraw(rp, unit) == -1)
- return -1;
- SRready(rp);
- if(SRinquiry(rp) >= 0){
- switch(rp->inquiry[0]){
- default:
- fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]);
- rp->status = Status_SW;
- break;
- case 0x00: /* Direct access (disk) */
- case 0x05: /* CD-ROM */
- case 0x07: /* rewriteable MO */
- if(dirdevopen(rp) == -1)
- break;
- return 0;
- case 0x01: /* Sequential eg: tape */
- rp->flags |= Fseqdev;
- if(seqdevopen(rp) == -1)
- break;
- return 0;
- case 0x02: /* Printer */
- rp->flags |= Fprintdev;
- return 0;
- case 0x04: /* Worm */
- rp->flags |= Fwormdev;
- if(wormdevopen(rp) == -1)
- break;
- return 0;
- case 0x08: /* medium-changer */
- rp->flags |= Fchanger;
- return 0;
- }
- }
- SRclose(rp);
- return -1;
- }
|