1007 lines
20 KiB
C
1007 lines
20 KiB
C
/* Copyright (c) 20XX 9front
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
/*
|
|
* This is /sys/src/cmd/scuzz/scsireq.c
|
|
* changed to add more debug support, to keep
|
|
* disk compiling without a scuzz that includes these changes.
|
|
* Also, this includes minor tweaks for usb:
|
|
* we set req.lun/unit to rp->lun/unit in SRreqsense
|
|
* we set the rp->sense[0] bit Sd0valid in SRreqsense
|
|
* This does not use libdisk to retrieve the scsi error to make
|
|
* user see the diagnostics if we boot with debug enabled.
|
|
*
|
|
* BUGS:
|
|
* no luns
|
|
* and incomplete in many other ways
|
|
*/
|
|
|
|
#include <u.h>
|
|
#include <lib9.h>
|
|
#include <9P2000.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;
|
|
|
|
static char *scmdnames[256] = {
|
|
[ScmdTur] "Tur",
|
|
[ScmdRewind] "Rewind",
|
|
[ScmdRsense] "Rsense",
|
|
[ScmdFormat] "Format",
|
|
[ScmdRblimits] "Rblimits",
|
|
[ScmdRead] "Read",
|
|
[ScmdWrite] "Write",
|
|
[ScmdSeek] "Seek",
|
|
[ScmdFmark] "Fmark",
|
|
[ScmdSpace] "Space",
|
|
[ScmdInq] "Inq",
|
|
[ScmdMselect6] "Mselect6",
|
|
[ScmdMselect10] "Mselect10",
|
|
[ScmdMsense6] "Msense6",
|
|
[ScmdMsense10] "Msense10",
|
|
[ScmdStart] "Start",
|
|
[ScmdRcapacity] "Rcapacity",
|
|
[ScmdRcapacity16] "Rcap16",
|
|
[ScmdExtread] "Extread",
|
|
[ScmdExtwrite] "Extwrite",
|
|
[ScmdExtseek] "Extseek",
|
|
|
|
[ScmdSynccache] "Synccache",
|
|
[ScmdRTOC] "RTOC",
|
|
[ScmdRdiscinfo] "Rdiscinfo",
|
|
[ScmdRtrackinfo] "Rtrackinfo",
|
|
[ScmdReserve] "Reserve",
|
|
[ScmdBlank] "Blank",
|
|
|
|
[ScmdCDpause] "CDpause",
|
|
[ScmdCDstop] "CDstop",
|
|
[ScmdCDplay] "CDplay",
|
|
[ScmdCDload] "CDload",
|
|
[ScmdCDscan] "CDscan",
|
|
[ScmdCDstatus] "CDstatus",
|
|
[Scmdgetconf] "getconf",
|
|
};
|
|
|
|
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.lun = rp->lun;
|
|
req.unit = rp->unit;
|
|
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;
|
|
if(status != -1)
|
|
rp->sense[0] |= Sd0valid;
|
|
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;
|
|
}
|
|
|
|
extern int diskdebug;
|
|
|
|
int32_t
|
|
SRread(ScsiReq *rp, void *buf, int32_t nbytes)
|
|
{
|
|
uint8_t cmd[10];
|
|
int32_t n;
|
|
|
|
if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
|
|
if(diskdebug)
|
|
if (nbytes % rp->lbsize)
|
|
fprint(2, "disk: i/o size %ld %% %ld != 0\n",
|
|
nbytes, rp->lbsize);
|
|
else
|
|
fprint(2, "disk: i/o size %ld > %d\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(!(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(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
|
|
if(diskdebug)
|
|
if (nbytes % rp->lbsize)
|
|
fprint(2, "disk: i/o size %ld %% %ld != 0\n",
|
|
nbytes, rp->lbsize);
|
|
else
|
|
fprint(2, "disk: i/o size %ld > %d\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(diskdebug)
|
|
fprint(2, "disk: 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) {
|
|
rp->offset = offset;
|
|
return 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] = 36;
|
|
rp->cmd.p = cmd;
|
|
rp->cmd.count = sizeof cmd;
|
|
memset(rp->inquiry, 0, sizeof rp->inquiry);
|
|
rp->data.p = rp->inquiry;
|
|
rp->data.count = 36;
|
|
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);
|
|
}
|
|
|
|
int32_t
|
|
SRrcapacity16(ScsiReq *rp, uint8_t *data)
|
|
{
|
|
uint8_t cmd[16];
|
|
uint32_t i;
|
|
|
|
i = 32;
|
|
memset(cmd, 0, sizeof cmd);
|
|
cmd[0] = ScmdRcapacity16;
|
|
cmd[1] = 0x10;
|
|
cmd[10] = i>>24;
|
|
cmd[11] = i>>16;
|
|
cmd[12] = i>>8;
|
|
cmd[13] = i;
|
|
|
|
rp->cmd.p = cmd;
|
|
rp->cmd.count = sizeof cmd;
|
|
rp->data.p = data;
|
|
rp->data.count = i;
|
|
rp->data.write = 0;
|
|
return SRrequest(rp);
|
|
}
|
|
|
|
void
|
|
scsidebug(int d)
|
|
{
|
|
debug = d;
|
|
if(debug)
|
|
fprint(2, "scsidebug on\n");
|
|
}
|
|
|
|
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(jehanne_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("");
|
|
// sys_alarm(5*1000);
|
|
if(data->write)
|
|
n = jehanne_write(fd, data->p, data->count);
|
|
else {
|
|
n = jehanne_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);
|
|
}
|
|
// sys_alarm(0);
|
|
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 = jehanne_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.2uX: data transfer: %r\n",
|
|
*status);
|
|
return n;
|
|
}
|
|
|
|
static char*
|
|
seprintcmd(char *s, char* e, char *cmd, int count, int args)
|
|
{
|
|
uint32_t c;
|
|
|
|
if(count < 6)
|
|
return seprint(s, e, "<short cmd>");
|
|
c = cmd[0];
|
|
if(scmdnames[c] != nil)
|
|
s = seprint(s, e, "%s", scmdnames[c]);
|
|
else
|
|
s = seprint(s, e, "cmd:%#02uX", c);
|
|
if(args != 0)
|
|
switch(c){
|
|
case ScmdRsense:
|
|
case ScmdInq:
|
|
case ScmdMselect6:
|
|
case ScmdMsense6:
|
|
s = seprint(s, e, " sz %d", cmd[4]);
|
|
break;
|
|
case ScmdSpace:
|
|
s = seprint(s, e, " code %d", cmd[1]);
|
|
break;
|
|
case ScmdStart:
|
|
s = seprint(s, e, " code %d", cmd[4]);
|
|
break;
|
|
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static char*
|
|
seprintdata(char *s, char *se, uint8_t *p, int count)
|
|
{
|
|
int i;
|
|
|
|
if(count == 0)
|
|
return s;
|
|
for(i = 0; i < 20 && i < count; i++)
|
|
s = seprint(s, se, " %02x", p[i]);
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
SRdumpReq(ScsiReq *rp)
|
|
{
|
|
char buf[128];
|
|
char *s;
|
|
char *se;
|
|
|
|
se = buf+sizeof(buf);
|
|
s = seprint(buf, se, "lun %d ", rp->lun);
|
|
s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 1);
|
|
s = seprint(s, se, " [%ld]", rp->data.count);
|
|
if(rp->cmd.write)
|
|
seprintdata(s, se, rp->data.p, rp->data.count);
|
|
fprint(2, "scsi⇒ %s\n", buf);
|
|
}
|
|
|
|
static void
|
|
SRdumpRep(ScsiReq *rp)
|
|
{
|
|
char buf[128];
|
|
char *s;
|
|
char *se;
|
|
|
|
se = buf+sizeof(buf);
|
|
s = seprint(buf, se, "lun %d ", rp->lun);
|
|
s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 0);
|
|
switch(rp->status){
|
|
case STok:
|
|
s = seprint(s, se, " good [%ld] ", rp->data.count);
|
|
if(rp->cmd.write == 0)
|
|
s = seprintdata(s, se, rp->data.p, rp->data.count);
|
|
break;
|
|
case STnomem:
|
|
s = seprint(s, se, " buffer allocation failed");
|
|
break;
|
|
case STharderr:
|
|
s = seprint(s, se, " controller error");
|
|
break;
|
|
case STtimeout:
|
|
s = seprint(s, se, " bus timeout");
|
|
break;
|
|
case STcheck:
|
|
s = seprint(s, se, " check condition");
|
|
break;
|
|
case STcondmet:
|
|
s = seprint(s, se, " condition met/good");
|
|
break;
|
|
case STbusy:
|
|
s = seprint(s, se, " busy");
|
|
break;
|
|
case STintok:
|
|
s = seprint(s, se, " intermediate/good");
|
|
break;
|
|
case STintcondmet:
|
|
s = seprint(s, se, " intermediate/condition met/good");
|
|
break;
|
|
case STresconf:
|
|
s = seprint(s, se, " reservation conflict");
|
|
break;
|
|
case STterminated:
|
|
s = seprint(s, se, " command terminated");
|
|
break;
|
|
case STqfull:
|
|
s = seprint(s, se, " queue full");
|
|
break;
|
|
default:
|
|
s = seprint(s, se, " sts=%#x", rp->status);
|
|
}
|
|
USED(s);
|
|
fprint(2, "scsi← %s\n", buf);
|
|
}
|
|
|
|
static char*
|
|
scsierr(ScsiReq *rp)
|
|
{
|
|
int ec;
|
|
|
|
switch(rp->status){
|
|
case 0:
|
|
return "";
|
|
case Status_SD:
|
|
ec = (rp->sense[12] << 8) | rp->sense[13];
|
|
return scsierrmsg(ec);
|
|
case Status_SW:
|
|
return "software error";
|
|
case Status_BADARG:
|
|
return "bad argument";
|
|
case Status_RO:
|
|
return "device is read only";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static void
|
|
SRdumpErr(ScsiReq *rp)
|
|
{
|
|
char buf[128];
|
|
char *se;
|
|
|
|
se = buf+sizeof(buf);
|
|
seprintcmd(buf, se, (char*)rp->cmd.p, rp->cmd.count, 0);
|
|
print("\t%s status: %s\n", buf, scsierr(rp));
|
|
}
|
|
|
|
int32_t
|
|
SRrequest(ScsiReq *rp)
|
|
{
|
|
int32_t n;
|
|
int status;
|
|
|
|
retry:
|
|
if(debug)
|
|
SRdumpReq(rp);
|
|
if(rp->flags&Fusb)
|
|
n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status);
|
|
else
|
|
n = request(rp->fd, &rp->cmd, &rp->data, &status);
|
|
rp->status = status;
|
|
if(status == STok)
|
|
rp->data.count = n;
|
|
if(debug)
|
|
SRdumpRep(rp);
|
|
switch(status){
|
|
case STok:
|
|
break;
|
|
case STcheck:
|
|
if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1)
|
|
rp->status = Status_SD;
|
|
if(debug || exabyte)
|
|
SRdumpErr(rp);
|
|
werrstr("%s", scsierr(rp));
|
|
return -1;
|
|
case STbusy:
|
|
sleep(1000); /* TODO: try a shorter sleep? */
|
|
goto retry;
|
|
default:
|
|
if(debug || exabyte)
|
|
SRdumpErr(rp);
|
|
werrstr("%s", scsierr(rp));
|
|
return -1;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int
|
|
SRclose(ScsiReq *rp)
|
|
{
|
|
if((rp->flags & Fopen) == 0){
|
|
if(diskdebug)
|
|
fprint(2, "disk: closing closed file\n");
|
|
rp->status = Status_BADARG;
|
|
return -1;
|
|
}
|
|
sys_close(rp->fd);
|
|
rp->flags = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
dirdevopen(ScsiReq *rp)
|
|
{
|
|
uint64_t blocks;
|
|
uint8_t data[8+4+20]; /* 16-byte result: lba, blksize, reserved */
|
|
|
|
memset(data, 0, sizeof data);
|
|
if(SRstart(rp, 1) == -1 || SRrcapacity(rp, data) == -1)
|
|
return -1;
|
|
rp->lbsize = GETBELONG(data+4);
|
|
blocks = GETBELONG(data);
|
|
if(debug)
|
|
fprint(2, "disk: dirdevopen: 10-byte logical block size %lud, "
|
|
"# blocks %llud\n", rp->lbsize, blocks);
|
|
if(blocks == 0xffffffff){
|
|
if(SRrcapacity16(rp, data) == -1)
|
|
return -1;
|
|
rp->lbsize = GETBELONG(data + 8);
|
|
blocks = (int64_t)GETBELONG(data)<<32 | GETBELONG(data + 4);
|
|
if(debug)
|
|
fprint(2, "disk: dirdevopen: 16-byte logical block size"
|
|
" %lud, # blocks %llud\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, "disk: seqdevopen: 10-byte logical block size %lud\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, "disk: wormdevopen: 10-byte logical block size %lud\n",
|
|
rp->lbsize);
|
|
return status;
|
|
}
|
|
|
|
int
|
|
SRopenraw(ScsiReq *rp, char *unit)
|
|
{
|
|
char name[128];
|
|
|
|
if(rp->flags & Fopen){
|
|
if(diskdebug)
|
|
fprint(2, "disk: opening open file\n");
|
|
rp->status = Status_BADARG;
|
|
return -1;
|
|
}
|
|
memset(rp, 0, sizeof *rp);
|
|
rp->unit = unit;
|
|
|
|
snprint(name, sizeof name, "%s/raw", unit);
|
|
if((rp->fd = sys_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 Devdir:
|
|
case Devcd:
|
|
case Devmo:
|
|
if(dirdevopen(rp) == -1)
|
|
break;
|
|
return 0;
|
|
|
|
case Devseq:
|
|
rp->flags |= Fseqdev;
|
|
if(seqdevopen(rp) == -1)
|
|
break;
|
|
return 0;
|
|
|
|
case Devprint:
|
|
rp->flags |= Fprintdev;
|
|
return 0;
|
|
|
|
case Devworm:
|
|
rp->flags |= Fwormdev;
|
|
if(wormdevopen(rp) == -1)
|
|
break;
|
|
return 0;
|
|
|
|
case Devjuke:
|
|
rp->flags |= Fchanger;
|
|
return 0;
|
|
}
|
|
}
|
|
SRclose(rp);
|
|
return -1;
|
|
}
|