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