/* * Native implementation of cdrdao's SCSI interface for Irix 6. * Copyright (C) by Edgar Fuß, Bonn, May 2003, July 2007. * Do with this whatever you like, as long as you are either me or you keep * this message intact and both * - acknowledge that I wrote it for cdrdao in the first place, and * - don't blame me if it doesn't do what you like or expect. * These routines do exactly what they do. If that's not what you expect them * or would like them to do, don't complain with me, the cdrdao project, my * neighbour's brother-in-law or anybody else, but rewrite them to your taste. */ #include #include #include #include #include #include #include #include #include #include "ScsiIf.h" extern void message(int level, const char *fmt, ...); #include "decodeSense.cc" #define MAX_SCAN 32 #define SENSE_MAX 256 class ScsiIfImpl { public: char *name_; int fd_; long timeout_; /* in ms */ struct dsreq dsreq_; char *error_; unsigned char sensebuf_[SENSE_MAX]; }; ScsiIf::ScsiIf(const char *name) { #define PREFIX "/dev/scsi/" #define PREFIX_SC "/dev/scsi/sc" int len; int bus, targ, lun, count; impl_ = new ScsiIfImpl; len = strlen(name); if (len == 0) { impl_->name_ = NULL; } else if (sscanf(name, "%i,%i,%i%n", &bus, &targ, &lun, &count) == 3 && count == len) { if ((bus < 0) || (targ < 0) || (lun < 0)) impl_->name_ = NULL; else { impl_->name_ = new char[strlen(PREFIX_SC) + len + 1]; sprintf(impl_->name_, "%s%dd%dl%d", PREFIX_SC, bus, targ, lun); } } else if (strncmp(name, "/", 1) == 0 || strncmp(name, "./", 2) == 0 || strncmp(name, "../", 3) == 0) { impl_->name_ = new char[len + 1]; strcpy(impl_->name_, name); } else { impl_->name_ = new char[strlen(PREFIX) + len + 1]; strcpy(impl_->name_, PREFIX); strcat(impl_->name_, name); } impl_->fd_ = -1; impl_->timeout_ = 10*1000; impl_->error_ = NULL; maxDataLen_ = 64 * 1024; vendor_[0] = 0; product_[0] = 0; revision_[0] = 0; #undef PREFIX } ScsiIf::~ScsiIf() { if (impl_->fd_ >= 0) (void)close(impl_->fd_); if (impl_->name_ != NULL) delete[] impl_->name_; if (impl_->error_ != NULL) delete[] impl_->error_; delete impl_; } int ScsiIf::init() { if (impl_->name_ == NULL) return 1; if ((impl_->fd_ = open(impl_->name_, O_RDWR, 0)) < 0) { message(-2, "init: %s", strerror(errno)); return 1; } if (inquiry()) return 2; return 0; } int ScsiIf::timeout(int t) { int ret = impl_->timeout_/1000; impl_->timeout_ = t*1000; return ret; } int ScsiIf::sendCmd(const unsigned char *cmd, int cmdLen, const unsigned char *dataOut, int dataOutLen, unsigned char *dataIn, int dataInLen, int showMessage) { #define ERROR(msg) {\ impl_->error_ = new char[9 + strlen(msg) + 1];\ strcpy(impl_->error_, "sendCmd: ");\ strcat(impl_->error_, msg);\ if (showMessage) printError();\ return 1;\ } if (impl_->error_ != NULL) { delete[] impl_->error_; impl_->error_ = NULL; } /* for printError: */ impl_->dsreq_.ds_cmdbuf = NULL; impl_->dsreq_.ds_cmdlen = 0; impl_->dsreq_.ds_databuf = NULL; impl_->dsreq_.ds_datalen = 0; impl_->dsreq_.ds_flags = 0; impl_->dsreq_.ds_time = impl_->timeout_; if (cmdLen == 0) ERROR("cmdLen == 0"); if (cmdLen > 255) ERROR("cmdLen > 255"); impl_->dsreq_.ds_cmdbuf = (caddr_t)cmd; impl_->dsreq_.ds_cmdlen = cmdLen; if (dataOut != NULL && dataIn != NULL) { ERROR("Out and In"); } else if (dataOut != NULL) { if (dataOutLen == 0) ERROR("dataOutLen == 0"); if (dataInLen != 0) ERROR("dataInLen != 0"); impl_->dsreq_.ds_flags |= DSRQ_WRITE; impl_->dsreq_.ds_databuf = (caddr_t)dataOut; impl_->dsreq_.ds_datalen = dataOutLen; } else if (dataIn != NULL) { if (dataInLen == 0) ERROR("dataInLen == 0"); if (dataOutLen !=0) ERROR("dataOutLen != 0"); impl_->dsreq_.ds_flags |= DSRQ_READ; impl_->dsreq_.ds_databuf = (caddr_t)dataIn; impl_->dsreq_.ds_datalen = dataInLen; } else { if (dataOutLen !=0 || dataInLen != 0) ERROR("dataLen != 0"); impl_->dsreq_.ds_databuf = NULL; impl_->dsreq_.ds_datalen = 0; } impl_->dsreq_.ds_senselen = SENSE_MAX; impl_->dsreq_.ds_sensebuf = (caddr_t)impl_->sensebuf_; impl_->dsreq_.ds_flags |= DSRQ_SENSE; if (ioctl(impl_->fd_, DS_ENTER, &impl_->dsreq_) < 0) { char str[80]; strcpy(str, "DS_ENTER: "); strcat(str, strerror(errno)); ERROR(str); } if (impl_->dsreq_.ds_ret == 0 || impl_->dsreq_.ds_ret == DSRT_OK || impl_->dsreq_.ds_ret == DSRT_SHORT) return 0; else if (impl_->dsreq_.ds_ret == DSRT_SENSE) { if (showMessage) printError(); return 1; } else { if (showMessage) printError(); return 2; } #undef ERROR } const unsigned char *ScsiIf::getSense(int &len) const { len = impl_->dsreq_.ds_sensesent; return impl_->sensebuf_; } void ScsiIf::printError() { if (impl_->dsreq_.ds_cmdbuf != NULL) { char s[80]; char *p = s; int i; p += snprintf(p, s + sizeof(s) - p, "CDB="); for (i = 0; i < impl_->dsreq_.ds_cmdlen; i++) { p += snprintf(p, s + sizeof(s) - p, "%.2X ", impl_->dsreq_.ds_cmdbuf[i]); } p[-1] = ','; switch (impl_->dsreq_.ds_flags & (DSRQ_READ | DSRQ_WRITE)) { case DSRQ_READ: p += snprintf(p, s + sizeof(s) - p, " RD"); break; case DSRQ_WRITE: p += snprintf(p, s + sizeof(s) - p, " WR"); break; case DSRQ_READ | DSRQ_WRITE: p += snprintf(p, s + sizeof(s) - p, " RW"); break; } p += snprintf(p, s + sizeof(s) - p, ", BUF=%p", impl_->dsreq_.ds_databuf); p += snprintf(p, s + sizeof(s) - p, ", LEN=%lu", impl_->dsreq_.ds_datalen); p += snprintf(p, s + sizeof(s) - p, ", TO=%lu", impl_->dsreq_.ds_time); message(-2, s); } if (impl_->error_ != NULL) { message(-2, impl_->error_); } else { switch (impl_->dsreq_.ds_ret) { case DSRT_DEVSCSI: message(-2,"devscsi failure"); break; case DSRT_MULT: message(-2,"request rejected"); break; case DSRT_CANCEL: message(-2,"request cancelled"); break; case DSRT_REVCODE: message(-2,"obsolete"); break; case DSRT_AGAIN: message(-2,"try again"); break; case DSRT_HOST: message(-2,"host failure"); break; case DSRT_NOSEL: message(-2,"no select"); break; case DSRT_SHORT: message(-2,"short transfer"); break; case DSRT_OK: message(-2,"complete transfer"); break; case DSRT_SENSE: /* message(-2,"sense"); */ decodeSense(impl_->sensebuf_, impl_->dsreq_.ds_sensesent); break; case DSRT_NOSENSE: message(-2,"sense error"); break; case DSRT_TIMEOUT: message(-2,"timeout"); break; case DSRT_LONG: message(-2,"overrun"); break; case DSRT_PROTO: message(-2,"protocol failure"); break; case DSRT_EBSY: message(-2,"busy lost"); break; case DSRT_REJECT: message(-2,"mesage reject"); break; case DSRT_PARITY: message(-2,"parity error"); break; case DSRT_MEMORY: message(-2,"memory error"); break; case DSRT_CMDO: message(-2,"command error"); break; case DSRT_STAI: message(-2,"status error"); break; case DSRT_UNIMPL: message(-2,"not implemented"); break; default: message(-2, "undefined ds_ret"); break; } switch (impl_->dsreq_.ds_status) { case 0x00: message(-2, "GOOD"); break; case 0x02: message(-2, "CHECK CONDITION"); break; case 0x04: message(-2, "CONDITION MET"); break; case 0x08: message(-2, "BUSY"); break; case 0x10: message(-2, "INTERMEDIATE"); break; case 0x14: message(-2, "INTERMEDIATE, CONDITION MET"); break; case 0x18: message(-2, "RESERVATION CONFLICT"); break; case 0x22: message(-2, "COMMAND TERMINATED"); break; case 0x28: message(-2, "QUEUE FULL"); break; case 0xff: break; default: message(-2, "undefined status"); } } } int inq(int fd, void *sensebuf, int senselen, char *vend, char *prod, char *rev) { char buf[44]; char cmd[] = {0x12, 0, 0, 0, sizeof(buf), 0}; struct dsreq dsreq = { /* flags */ DSRQ_READ, /* time */ 1000, /* private */ 0, /* cmdbuf */ (caddr_t)cmd, /* cmdlen */ sizeof(cmd), /* databuf */ (caddr_t)buf, /* datalen */ sizeof(buf), /* sensebuf */ (caddr_t)sensebuf, /* senselen */ senselen, /* iovbuf */ NULL, /* iovlen */ 0, /* link */ NULL, /* sync */ 0, /* revcode */ 0, /* ret */ 0, /* status */ 0, /* msg */ 0, /* cmdsent */ 0, /* datasent */ 0, /* sensesent */ 0 }; char *p, *q; if (sensebuf != NULL && senselen > 0) dsreq.ds_flags |= DSRQ_SENSE; if (ioctl(fd, DS_ENTER, &dsreq) < 0 || (dsreq.ds_ret != 0 && dsreq.ds_ret != DSRT_OK && dsreq.ds_ret != DSRT_SHORT)) { vend[0] = prod[0] = rev[0] = '\0'; return 1; } p = buf + 8; q = buf + 16; while (q > p && q[-1] == ' ') q--; memcpy(vend, p, q - p); vend[q - p] = '\0'; p = buf + 16; q = buf + 32; while (q > p && q[-1] == ' ') q--; memcpy(prod, p, q - p); prod[q - p] = '\0'; p = buf + 32; q = buf + 36; while (q > p && q[-1] == ' ') q--; memcpy(rev, p, q - p); rev[q - p] = '\0'; return 0; } int ScsiIf::inquiry() { return inq(impl_->fd_, impl_->sensebuf_, SENSE_MAX, vendor_, product_, revision_); } ScsiIf::ScanData *ScsiIf::scan(int *len) { DIR *dirp; struct dirent *dp; int l; int fd; ScanData *scanData; char s[35]; int bus, targ, lun, count; scanData = new ScanData[MAX_SCAN]; *len = 0; if ((dirp = opendir("/dev/scsi")) == 0) { *len = 0; return NULL; } while ((dp = readdir(dirp)) != NULL) { l = strlen(dp->d_name); if (*len < MAX_SCAN && l > 2 && sscanf(dp->d_name, "sc%dd%dl%d%n", &bus, &targ, &lun, &count) == 3 && count == l) { scanData[*len].bus = bus; scanData[*len].id = targ; scanData[*len].lun = lun; strcpy(s, "/dev/scsi/"); strcat(s, dp->d_name); if ((fd = open(s, O_RDWR, 0)) >= 0) { if (inq(fd, NULL, 0, scanData[*len].vendor, scanData[*len].product, scanData[*len].revision) == 0) (*len)++; } (void)close(fd); } } closedir(dirp); return scanData; } #include "ScsiIf-common.cc"