/* * Native implementation of cdrdao's SCSI interface for NetBSD. * 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 and NetBSD 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 /* avoid ../trackdb/util.h */ #include "/usr/include/util.h" #include #include "ScsiIf.h" /* can't include trackdb/util.h */ extern void message(int level, const char *fmt, ...); #include "decodeSense.cc" #define MAX_SCAN 32 class ScsiIfImpl { public: char *name_; int fd_; long timeout_; /* in ms */ struct scsireq screq_; char *error_; }; ScsiIf::ScsiIf(const char *name) { #define PREFIX "/dev/" int len; impl_ = new ScsiIfImpl; len = strlen(name); if (len == 0) { impl_->name_ = NULL; } 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 if (isdigit(name[len-1]) && (strncmp(name, "sd", 2) == 0 || strncmp(name, "rsd", 3) == 0 || strncmp(name, "cd", 2) == 0 || strncmp(name, "rcd", 3) == 0)) { impl_->name_ = new char[strlen(PREFIX) + len + 1 + 1]; strcpy(impl_->name_, PREFIX); strcat(impl_->name_, name); impl_->name_[strlen(PREFIX) + len] = 'a' + getrawpartition(); impl_->name_[strlen(PREFIX) + len + 1] = '\0'; } else { impl_->name_ = new char[strlen(PREFIX) + len + 1]; strcpy(impl_->name_, PREFIX); strcat(impl_->name_, name); } impl_->fd_ = -1; impl_->timeout_ = 5*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; } // sends a scsi command and receives data // return 0: OK // 1: scsi command failed (os level, no sense data available) // 2: scsi command failed (sense data available) 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_->screq_.cmdlen = 0; impl_->screq_.databuf = NULL; impl_->screq_.datalen = 0; impl_->screq_.flags = 0; impl_->screq_.timeout = impl_->timeout_; if (cmdLen > 16) ERROR("cmdLen > 16"); if (cmdLen == 0) ERROR("cmdLen == 0"); memcpy(impl_->screq_.cmd, cmd, cmdLen); impl_->screq_.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_->screq_.flags |= SCCMD_WRITE; impl_->screq_.databuf = (caddr_t)dataOut; impl_->screq_.datalen = dataOutLen; } else if (dataIn != NULL) { if (dataInLen == 0) ERROR("dataInLen == 0"); if (dataOutLen !=0) ERROR("dataOutLen != 0"); impl_->screq_.flags |= SCCMD_READ; impl_->screq_.databuf = (caddr_t)dataIn; impl_->screq_.datalen = dataInLen; } else { if (dataOutLen !=0 || dataInLen != 0) ERROR("dataLen != 0"); impl_->screq_.databuf = NULL; impl_->screq_.datalen = 0; } impl_->screq_.senselen = SENSEBUFLEN; if (ioctl(impl_->fd_, SCIOCCOMMAND, &impl_->screq_) < 0) { char str[80]; strcpy(str, "SCIOCOMMAND: "); strcat(str, strerror(errno)); ERROR(str); } if (impl_->screq_.retsts == SCCMD_OK && impl_->screq_.status == 0) return 0; else if (impl_->screq_.retsts == SCCMD_SENSE && impl_->screq_.status == 0) { if (showMessage) printError(); return 1; } else { if (showMessage) printError(); return 2; } #undef ERROR } const unsigned char *ScsiIf::getSense(int &len) const { len = impl_->screq_.senselen_used; return impl_->screq_.sense; } void ScsiIf::printError() { if (impl_->screq_.cmdlen > 0) { char s[80]; char *p = s; int i; p += snprintf(p, s + sizeof(s) - p, "CDB="); for (i = 0; i < impl_->screq_.cmdlen; i++) { p += snprintf(p, s + sizeof(s) - p, "%.2X ", impl_->screq_.cmd[i]); } p[-1] = ','; switch (impl_->screq_.flags & (SCCMD_READ | SCCMD_WRITE)) { case SCCMD_READ: p += snprintf(p, s + sizeof(s) - p, " RD"); break; case SCCMD_WRITE: p += snprintf(p, s + sizeof(s) - p, " WR"); break; case SCCMD_READ | SCCMD_WRITE: p += snprintf(p, s + sizeof(s) - p, " RW"); break; } p += snprintf(p, s + sizeof(s) - p, ", BUF=%p", impl_->screq_.databuf); p += snprintf(p, s + sizeof(s) - p, ", LEN=%lu", impl_->screq_.datalen); p += snprintf(p, s + sizeof(s) - p, ", TO=%lu", impl_->screq_.timeout); message(-2, s); } if (impl_->error_ != NULL) { message(-2, impl_->error_); } else switch (impl_->screq_.retsts) { case SCCMD_OK: switch (impl_->screq_.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; default: message(-2, "undefined status"); } break; case SCCMD_TIMEOUT: message(-2, "timeout"); break; case SCCMD_BUSY: message(-2, "busy"); break; case SCCMD_SENSE: decodeSense(impl_->screq_.sense, impl_->screq_.senselen_used); break; case SCCMD_UNKNOWN: message(-2, "unknown error"); break; default: message(-2, "undefined retsts"); break; } } int inq(int fd, char *vend, char *prod, char *rev) { char buf[44]; struct scsireq screq = { /* flags */ SCCMD_READ, /* timeout */ 1000, /* cmd */ {0x12, 0, 0, 0, sizeof(buf), 0}, /* cmdlen */ 6, /* databuf */ (caddr_t)&buf, /* datalen */ sizeof(buf), /* datalen_used */ 0, /* sense */ {}, /* senselen */ SENSEBUFLEN, /* senselen_used */ 0, /* status */ 0, /* retsts */ 0, /* error */ 0 }; char *p, *q; if (ioctl(fd, SCIOCCOMMAND, &screq) < 0 || screq.status != 0 || screq.retsts != SCCMD_OK) { 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_, vendor_, product_, revision_); } ScsiIf::ScanData *ScsiIf::scan(int *len) { DIR *dirp; struct dirent *dp; char c; int l; int fd; struct scsi_addr saddr; ScanData *scanData; char *s; scanData = new ScanData[MAX_SCAN]; *len = 0; c = 'a' + getrawpartition(); if ((dirp = opendir("/dev")) == 0) { *len = 0; return NULL; } while ((dp = readdir(dirp)) != NULL) { l = strlen(dp->d_name); if (*len < MAX_SCAN && l > 2 && (((strncmp(dp->d_name, "rsd", 3) == 0 || strncmp(dp->d_name, "rcd", 3) == 0) && isdigit(dp->d_name[l-2]) && dp->d_name[l-1] == c) || ((strncmp(dp->d_name, "enrst", 5) == 0 || strncmp(dp->d_name, "ch", 2) == 0 || strncmp(dp->d_name, "enss", 4) == 0 || strncmp(dp->d_name, "uk", 2) == 0) && isdigit(dp->d_name[l-1])))) { s = new char[5 + l + 1]; strcpy(s, "/dev/"); strcat(s, dp->d_name); if ((fd = open(s, O_RDWR, 0)) >= 0) { if (ioctl(fd, SCIOCIDENTIFY, &saddr) >= 0) { switch (saddr.type) { case TYPE_SCSI: scanData[*len].bus = saddr.addr.scsi.scbus; scanData[*len].id = saddr.addr.scsi.target; scanData[*len].lun = saddr.addr.scsi.lun; break; case TYPE_ATAPI: scanData[*len].bus = saddr.addr.atapi.atbus; scanData[*len].id = saddr.addr.atapi.drive; scanData[*len].lun = -1; break; default: scanData[*len].bus = scanData[*len].id = scanData[*len].lun = -1; } if (inq(fd, scanData[*len].vendor, scanData[*len].product, scanData[*len].revision) == 0) (*len)++; } (void)close(fd); } delete[] s; } } closedir(dirp); return scanData; } #include "ScsiIf-common.cc"