1/* 2 * This program is free software; you can redistribute it and/or 3 * modify it under the terms of the GNU General Public License as 4 * published by the Free Software Foundation; either version 2 of 5 * the License, or (at your option) any later version. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 15 * MA 02111-1307 USA 16 */ 17/* 18 * Copyright (c) 2005-2007 Douglas Gilbert. 19 * All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 3. The name of the author may not be used to endorse or promote products 30 * derived from this software without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 * 44 */ 45 46/* version 1.03 2007/4/3 */ 47 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51#include <ctype.h> 52#include <unistd.h> 53#include <errno.h> 54#include <fcntl.h> 55#include <sys/ioctl.h> 56#include <sys/types.h> 57#include <sys/stat.h> 58 59#include "sg_pt.h" 60#include "sg_lib.h" 61#include "sg_linux_inc.h" 62 63 64#define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */ 65 66struct sg_pt_linux_scsi { 67 struct sg_io_hdr io_hdr; 68 int in_err; 69 int os_err; 70}; 71 72struct sg_pt_base { 73 struct sg_pt_linux_scsi impl; 74}; 75 76 77 78/* Returns >= 0 if successful. If error in Unix returns negated errno. */ 79int scsi_pt_open_device(const char * device_name, int read_only, 80 int verbose) 81{ 82 int oflags = O_NONBLOCK; 83 int fd; 84 85 oflags |= (read_only ? O_RDONLY : O_RDWR); 86 if (verbose > 1) { 87 if (NULL == sg_warnings_strm) 88 sg_warnings_strm = stderr; 89 fprintf(sg_warnings_strm, "open %s with flags=0x%x\n", device_name, 90 oflags); 91 } 92 fd = open(device_name, oflags); 93 if (fd < 0) 94 fd = -errno; 95 return fd; 96} 97 98/* Returns 0 if successful. If error in Unix returns negated errno. */ 99int scsi_pt_close_device(int device_fd) 100{ 101 int res; 102 103 res = close(device_fd); 104 if (res < 0) 105 res = -errno; 106 return res; 107} 108 109 110struct sg_pt_base * construct_scsi_pt_obj() 111{ 112 struct sg_pt_linux_scsi * ptp; 113 114 ptp = (struct sg_pt_linux_scsi *) 115 malloc(sizeof(struct sg_pt_linux_scsi)); 116 if (ptp) { 117 memset(ptp, 0, sizeof(struct sg_pt_linux_scsi)); 118 ptp->io_hdr.interface_id = 'S'; 119 ptp->io_hdr.dxfer_direction = SG_DXFER_NONE; 120 } 121 return (struct sg_pt_base *)ptp; 122} 123 124void destruct_scsi_pt_obj(struct sg_pt_base * vp) 125{ 126 struct sg_pt_linux_scsi * ptp = &vp->impl; 127 128 if (ptp) 129 free(ptp); 130} 131 132void set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, 133 int cdb_len) 134{ 135 struct sg_pt_linux_scsi * ptp = &vp->impl; 136 137 if (ptp->io_hdr.cmdp) 138 ++ptp->in_err; 139 ptp->io_hdr.cmdp = (unsigned char *)cdb; 140 ptp->io_hdr.cmd_len = cdb_len; 141} 142 143void set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, 144 int max_sense_len) 145{ 146 struct sg_pt_linux_scsi * ptp = &vp->impl; 147 148 if (ptp->io_hdr.sbp) 149 ++ptp->in_err; 150 memset(sense, 0, max_sense_len); 151 ptp->io_hdr.sbp = sense; 152 ptp->io_hdr.mx_sb_len = max_sense_len; 153} 154 155void set_scsi_pt_data_in(struct sg_pt_base * vp, /* from device */ 156 unsigned char * dxferp, int dxfer_len) 157{ 158 struct sg_pt_linux_scsi * ptp = &vp->impl; 159 160 if (ptp->io_hdr.dxferp) 161 ++ptp->in_err; 162 if (dxfer_len > 0) { 163 ptp->io_hdr.dxferp = dxferp; 164 ptp->io_hdr.dxfer_len = dxfer_len; 165 ptp->io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; 166 } 167} 168 169void set_scsi_pt_data_out(struct sg_pt_base * vp, /* to device */ 170 const unsigned char * dxferp, int dxfer_len) 171{ 172 struct sg_pt_linux_scsi * ptp = &vp->impl; 173 174 if (ptp->io_hdr.dxferp) 175 ++ptp->in_err; 176 if (dxfer_len > 0) { 177 ptp->io_hdr.dxferp = (unsigned char *)dxferp; 178 ptp->io_hdr.dxfer_len = dxfer_len; 179 ptp->io_hdr.dxfer_direction = SG_DXFER_TO_DEV; 180 } 181} 182 183void set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id) 184{ 185 struct sg_pt_linux_scsi * ptp = &vp->impl; 186 187 ptp->io_hdr.pack_id = pack_id; 188} 189 190void set_scsi_pt_tag(struct sg_pt_base * vp, unsigned long long tag) 191{ 192 struct sg_pt_linux_scsi * ptp = &vp->impl; 193 194 ++ptp->in_err; 195 tag = tag; /* dummy to silence compiler */ 196} 197 198/* Note that task management function codes are transport specific */ 199void set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code) 200{ 201 struct sg_pt_linux_scsi * ptp = &vp->impl; 202 203 ++ptp->in_err; 204 tmf_code = tmf_code; /* dummy to silence compiler */ 205} 206 207void set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, 208 int priority) 209{ 210 struct sg_pt_linux_scsi * ptp = &vp->impl; 211 212 ++ptp->in_err; 213 attribute = attribute; /* dummy to silence compiler */ 214 priority = priority; /* dummy to silence compiler */ 215} 216 217int do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose) 218{ 219 struct sg_pt_linux_scsi * ptp = &vp->impl; 220 221 if (NULL == sg_warnings_strm) 222 sg_warnings_strm = stderr; 223 if (ptp->in_err) { 224 if (verbose) 225 fprintf(sg_warnings_strm, "Replicated or unused set_scsi_pt... " 226 "functions\n"); 227 return SCSI_PT_DO_BAD_PARAMS; 228 } 229 if (NULL == ptp->io_hdr.cmdp) { 230 if (verbose) 231 fprintf(sg_warnings_strm, "No SCSI command (cdb) given\n"); 232 return SCSI_PT_DO_BAD_PARAMS; 233 } 234 /* io_hdr.timeout is in milliseconds */ 235 ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : 236 DEF_TIMEOUT); 237 if (ptp->io_hdr.sbp && (ptp->io_hdr.sb_len_wr > 0)) 238 memset(ptp->io_hdr.sbp, 0, ptp->io_hdr.sb_len_wr); 239 if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) { 240 ptp->os_err = errno; 241 if (verbose) 242 fprintf(sg_warnings_strm, "ioctl(SG_IO) failed with os_err " 243 "(errno) = %d\n", ptp->os_err); 244 return -ptp->os_err; 245 } 246 return 0; 247} 248 249/* 250 * These defines are for constants that should be visible in the 251 * /usr/include/scsi directory (brought in by sg_linux_inc.h). 252 * Redefined and aliased here to decouple this code from 253 * sg_io_linux.h 254 */ 255#ifndef DRIVER_MASK 256#define DRIVER_MASK 0x0f 257#endif 258#ifndef SUGGEST_MASK 259#define SUGGEST_MASK 0xf0 260#endif 261#ifndef DRIVER_SENSE 262#define DRIVER_SENSE 0x08 263#endif 264#define SG_LIB_DRIVER_MASK DRIVER_MASK 265#define SG_LIB_SUGGEST_MASK SUGGEST_MASK 266#define SG_LIB_DRIVER_SENSE DRIVER_SENSE 267 268int get_scsi_pt_result_category(const struct sg_pt_base * vp) 269{ 270 const struct sg_pt_linux_scsi * ptp = &vp->impl; 271 int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK; 272 int scsi_st = ptp->io_hdr.status & 0x7e; 273 274 if (ptp->os_err) 275 return SCSI_PT_RESULT_OS_ERR; 276 else if (ptp->io_hdr.host_status) 277 return SCSI_PT_RESULT_TRANSPORT_ERR; 278 else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st)) 279 return SCSI_PT_RESULT_TRANSPORT_ERR; 280 else if ((SG_LIB_DRIVER_SENSE == dr_st) || 281 (SAM_STAT_CHECK_CONDITION == scsi_st) || 282 (SAM_STAT_COMMAND_TERMINATED == scsi_st)) 283 return SCSI_PT_RESULT_SENSE; 284 else if (scsi_st) 285 return SCSI_PT_RESULT_STATUS; 286 else 287 return SCSI_PT_RESULT_GOOD; 288} 289 290int get_scsi_pt_resid(const struct sg_pt_base * vp) 291{ 292 const struct sg_pt_linux_scsi * ptp = &vp->impl; 293 294 return ptp->io_hdr.resid; 295} 296 297int get_scsi_pt_status_response(const struct sg_pt_base * vp) 298{ 299 const struct sg_pt_linux_scsi * ptp = &vp->impl; 300 301 return ptp->io_hdr.status; 302} 303 304int get_scsi_pt_sense_len(const struct sg_pt_base * vp) 305{ 306 const struct sg_pt_linux_scsi * ptp = &vp->impl; 307 308 return ptp->io_hdr.sb_len_wr; 309} 310 311int get_scsi_pt_duration_ms(const struct sg_pt_base * vp) 312{ 313 const struct sg_pt_linux_scsi * ptp = &vp->impl; 314 315 return ptp->io_hdr.duration; 316} 317 318int get_scsi_pt_transport_err(const struct sg_pt_base * vp) 319{ 320 const struct sg_pt_linux_scsi * ptp = &vp->impl; 321 322 return (ptp->io_hdr.host_status << 8) + ptp->io_hdr.driver_status; 323} 324 325int get_scsi_pt_os_err(const struct sg_pt_base * vp) 326{ 327 const struct sg_pt_linux_scsi * ptp = &vp->impl; 328 329 return ptp->os_err; 330} 331 332static const char * linux_host_bytes[] = { 333 "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", 334 "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR", 335 "DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR", 336 "DID_IMM_RETRY", "DID_REQUEUE" 337}; 338 339#define LINUX_HOST_BYTES_SZ \ 340 (int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0])) 341 342static const char * linux_driver_bytes[] = { 343 "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", 344 "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", 345 "DRIVER_SENSE" 346}; 347 348#define LINUX_DRIVER_BYTES_SZ \ 349 (int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0])) 350 351static const char * linux_driver_suggests[] = { 352 "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", 353 "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN", 354 "SUGGEST_SENSE" 355}; 356 357#define LINUX_DRIVER_SUGGESTS_SZ \ 358 (int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0])) 359 360 361char * get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, 362 int max_b_len, char * b) 363{ 364 const struct sg_pt_linux_scsi * ptp = &vp->impl; 365 int ds = ptp->io_hdr.driver_status; 366 int hs = ptp->io_hdr.host_status; 367 int n, m; 368 char * cp = b; 369 int driv, sugg; 370 const char * driv_cp = "invalid"; 371 const char * sugg_cp = "invalid"; 372 373 m = max_b_len; 374 n = 0; 375 if (ptp->io_hdr.host_status) { 376 if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ)) 377 n = snprintf(cp, m, "Host_status=0x%02x is invalid\n", hs); 378 else 379 n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs, 380 linux_host_bytes[hs]); 381 } 382 m -= n; 383 if (m < 1) { 384 b[max_b_len - 1] = '\0'; 385 return b; 386 } 387 cp += n; 388 driv = ds & SG_LIB_DRIVER_MASK; 389 if (driv < LINUX_DRIVER_BYTES_SZ) 390 driv_cp = linux_driver_bytes[driv]; 391 sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4; 392 if (sugg < LINUX_DRIVER_SUGGESTS_SZ) 393 sugg_cp = linux_driver_suggests[sugg]; 394 n = snprintf(cp, m, "Driver_status=0x%02x [%s, %s]\n", ds, driv_cp, 395 sugg_cp); 396 m -= n; 397 if (m < 1) 398 b[max_b_len - 1] = '\0'; 399 return b; 400} 401 402char * get_scsi_pt_os_err_str(const struct sg_pt_base * vp, 403 int max_b_len, char * b) 404{ 405 const struct sg_pt_linux_scsi * ptp = &vp->impl; 406 const char * cp; 407 408 cp = safe_strerror(ptp->os_err); 409 strncpy(b, cp, max_b_len); 410 if ((int)strlen(cp) >= max_b_len) 411 b[max_b_len - 1] = '\0'; 412 return b; 413} 414