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) 2006-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.04 2007/04/3 */
47
48#include <stdio.h>
49#include <stdlib.h>
50#include <stddef.h>
51#include <string.h>
52#include <errno.h>
53#include <ctype.h>
54
55#include "sg_pt.h"
56#include "sg_lib.h"
57#include "sg_pt_win32.h"
58
59/* Use the Microsoft SCSI Pass Through (SPT) interface. It has two
60 * variants: "SPT" where data is double buffered; and "SPTD" where data
61 * pointers to the user space are passed to the OS. Only Windows
62 * 2000, 2003 and XP are supported (i.e. not 95,98 or ME).
63 * Currently there is no ASPI interface which relies on a dll
64 * from adpatec.
65 * This code uses cygwin facilities and is built in a cygwin
66 * shell. It can be run in a normal DOS shell if the cygwin1.dll
67 * file is put in an appropriate place.
68 */
69
70#define DEF_TIMEOUT 60       /* 60 seconds */
71#define MAX_OPEN_SIMULT 8
72#define WIN32_FDOFFSET 32
73
74struct sg_pt_handle {
75    int in_use;
76    HANDLE fh;
77    char adapter[32];
78    int bus;
79    int target;
80    int lun;
81};
82
83struct sg_pt_handle handle_arr[MAX_OPEN_SIMULT];
84
85struct sg_pt_win32_scsi {
86#ifdef SPTD
87    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
88#else
89    SCSI_PASS_THROUGH_WITH_BUFFERS swb;
90#endif
91    unsigned char * dxferp;
92    int dxfer_len;
93    unsigned char * sensep;
94    int sense_len;
95    int scsi_status;
96    int resid;
97    int sense_resid;
98    int in_err;
99    int os_err;                 /* pseudo unix error */
100    int transport_err;          /* windows error number */
101};
102
103struct sg_pt_base {
104    struct sg_pt_win32_scsi impl;
105};
106
107/* Returns >= 0 if successful. If error in Unix returns negated errno.
108 * Optionally accept leading "\\.\". If given something of the form
109 * "ScSi<num>:<bus>,<target>,<lun>" where the values in angle brackets
110 * are integers, then will attempt to open "\\.\SCSI<num>:" and save the
111 * other three values for the DeviceIoControl call. The trailing ".<lun>"
112 * is optionally and if not given 0 is assumed. Since "PhysicalDrive"
113 * is a lot of keystrokes, "PD" is accepted and converted to the longer
114 * form.
115 */
116int scsi_pt_open_device(const char * device_name,
117                        int read_only __attribute__ ((unused)),
118                        int verbose)
119{
120    int len, k, adapter_num, bus, target, lun, off, got_scsi_name;
121    int index, num, got_pd_name, pd_num;
122    struct sg_pt_handle * shp;
123    char buff[8];
124
125    if (NULL == sg_warnings_strm)
126        sg_warnings_strm = stderr;
127    /* lock */
128    for (k = 0; k < MAX_OPEN_SIMULT; k++)
129        if (0 == handle_arr[k].in_use)
130            break;
131    if (k == MAX_OPEN_SIMULT) {
132        if (verbose)
133            fprintf(sg_warnings_strm, "too many open handles "
134                    "(%d)\n", MAX_OPEN_SIMULT);
135        return -EMFILE;
136    } else
137        handle_arr[k].in_use = 1;
138    /* unlock */
139    index = k;
140    shp = handle_arr + index;
141    adapter_num = 0;
142    bus = 0;    /* also known as 'PathId' in MS docs */
143    target = 0;
144    lun = 0;
145    got_pd_name = 0;
146    got_scsi_name = 0;
147    len = strlen(device_name);
148    if ((len > 4) && (0 == strncmp("\\\\.\\", device_name, 4)))
149        off = 4;
150    else
151        off = 0;
152    if (len > (off + 2)) {
153        buff[0] = toupper(device_name[off + 0]);
154        buff[1] = toupper(device_name[off + 1]);
155        if (0 == strncmp("PD", buff, 2)) {
156            num = sscanf(device_name + off + 2, "%d", &pd_num);
157            if (1 == num)
158                got_pd_name = 1;
159        }
160        if (0 == got_pd_name) {
161            buff[2] = toupper(device_name[off + 2]);
162            buff[3] = toupper(device_name[off + 3]);
163            if (0 == strncmp("SCSI", buff, 4)) {
164                num = sscanf(device_name + off + 4, "%d:%d,%d,%d",
165                             &adapter_num, &bus, &target, &lun);
166                if (num < 3) {
167                    if (verbose)
168                        fprintf(sg_warnings_strm, "expected format like: "
169                                "'SCSI<port>:<bus>.<target>[.<lun>]'\n");
170                    shp->in_use = 0;
171                    return -EINVAL;
172                }
173                got_scsi_name = 1;
174            }
175        }
176    }
177    shp->bus = bus;
178    shp->target = target;
179    shp->lun = lun;
180    memset(shp->adapter, 0, sizeof(shp->adapter));
181    strncpy(shp->adapter, "\\\\.\\", 4);
182    if (got_pd_name)
183        snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5,
184                 "PhysicalDrive%d", pd_num);
185    else if (got_scsi_name)
186        snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5, "SCSI%d:",
187                 adapter_num);
188    else
189        snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5, "%s",
190                 device_name + off);
191    shp->fh = CreateFile(shp->adapter, GENERIC_READ | GENERIC_WRITE,
192                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
193                         OPEN_EXISTING, 0, NULL);
194    if (shp->fh == INVALID_HANDLE_VALUE) {
195        if (verbose)
196            fprintf(sg_warnings_strm, "Windows CreateFile error=%ld\n",
197                    GetLastError());
198        shp->in_use = 0;
199        return -ENODEV;
200    }
201    return index + WIN32_FDOFFSET;
202}
203
204
205/* Returns 0 if successful. If error in Unix returns negated errno. */
206int scsi_pt_close_device(int device_fd)
207{
208    struct sg_pt_handle * shp;
209    int index;
210
211    index = device_fd - WIN32_FDOFFSET;
212
213    if ((index < 0) || (index >= WIN32_FDOFFSET))
214        return -ENODEV;
215    shp = handle_arr + index;
216    CloseHandle(shp->fh);
217    shp->bus = 0;
218    shp->target = 0;
219    shp->lun = 0;
220    memset(shp->adapter, 0, sizeof(shp->adapter));
221    shp->in_use = 0;
222    return 0;
223}
224
225struct sg_pt_base * construct_scsi_pt_obj()
226{
227    struct sg_pt_win32_scsi * psp;
228
229    psp = (struct sg_pt_win32_scsi *)malloc(sizeof(struct sg_pt_win32_scsi));
230    if (psp) {
231        memset(psp, 0, sizeof(struct sg_pt_win32_scsi));
232        psp->swb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
233        psp->swb.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
234        psp->swb.spt.SenseInfoOffset =
235                offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
236        psp->swb.spt.TimeOutValue = DEF_TIMEOUT;
237    }
238    return (struct sg_pt_base *)psp;
239}
240
241void destruct_scsi_pt_obj(struct sg_pt_base * vp)
242{
243    struct sg_pt_win32_scsi * psp = &vp->impl;
244
245    if (psp) {
246        free(psp);
247    }
248}
249
250void set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
251                     int cdb_len)
252{
253    struct sg_pt_win32_scsi * psp = &vp->impl;
254
255    if (psp->swb.spt.CdbLength > 0)
256        ++psp->in_err;
257    if (cdb_len > (int)sizeof(psp->swb.spt.Cdb)) {
258        ++psp->in_err;
259        return;
260    }
261    memcpy(psp->swb.spt.Cdb, cdb, cdb_len);
262    psp->swb.spt.CdbLength = cdb_len;
263}
264
265void set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
266                       int sense_len)
267{
268    struct sg_pt_win32_scsi * psp = &vp->impl;
269
270    if (psp->sensep)
271        ++psp->in_err;
272    memset(sense, 0, sense_len);
273    psp->sensep = sense;
274    psp->sense_len = sense_len;
275}
276
277void set_scsi_pt_data_in(struct sg_pt_base * vp,             /* from device */
278                         unsigned char * dxferp, int dxfer_len)
279{
280    struct sg_pt_win32_scsi * psp = &vp->impl;
281
282    if (psp->dxferp)
283        ++psp->in_err;
284    if (dxfer_len > 0) {
285        psp->dxferp = dxferp;
286        psp->dxfer_len = dxfer_len;
287        psp->swb.spt.DataIn = SCSI_IOCTL_DATA_IN;
288    }
289}
290
291void set_scsi_pt_data_out(struct sg_pt_base * vp,            /* to device */
292                          const unsigned char * dxferp, int dxfer_len)
293{
294    struct sg_pt_win32_scsi * psp = &vp->impl;
295
296    if (psp->dxferp)
297        ++psp->in_err;
298    if (dxfer_len > 0) {
299        psp->dxferp = (unsigned char *)dxferp;
300        psp->dxfer_len = dxfer_len;
301        psp->swb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
302    }
303}
304
305void set_scsi_pt_packet_id(struct sg_pt_base * vp __attribute__ ((unused)),
306                           int pack_id __attribute__ ((unused)))
307{
308}
309
310void set_scsi_pt_tag(struct sg_pt_base * vp,
311                     unsigned long long tag __attribute__ ((unused)))
312{
313    struct sg_pt_win32_scsi * psp = &vp->impl;
314
315    ++psp->in_err;
316}
317
318void set_scsi_pt_task_management(struct sg_pt_base * vp,
319                                 int tmf_code __attribute__ ((unused)))
320{
321    struct sg_pt_win32_scsi * psp = &vp->impl;
322
323    ++psp->in_err;
324}
325
326void set_scsi_pt_task_attr(struct sg_pt_base * vp,
327                           int attrib __attribute__ ((unused)),
328                           int priority __attribute__ ((unused)))
329{
330    struct sg_pt_win32_scsi * psp = &vp->impl;
331
332    ++psp->in_err;
333}
334
335int do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs,
336               int verbose)
337{
338    int index = device_fd - WIN32_FDOFFSET;
339    struct sg_pt_win32_scsi * psp = &vp->impl;
340    struct sg_pt_handle * shp;
341    BOOL status;
342    ULONG returned;
343
344    if (NULL == sg_warnings_strm)
345        sg_warnings_strm = stderr;
346    if (psp->in_err) {
347        if (verbose)
348            fprintf(sg_warnings_strm, "Replicated or unused set_scsi_pt...\n");
349        return SCSI_PT_DO_BAD_PARAMS;
350    }
351    if (0 == psp->swb.spt.CdbLength) {
352        if (verbose)
353            fprintf(sg_warnings_strm, "No command (cdb) given\n");
354        return SCSI_PT_DO_BAD_PARAMS;
355    }
356
357    index = device_fd - WIN32_FDOFFSET;
358    if ((index < 0) || (index >= WIN32_FDOFFSET)) {
359        if (verbose)
360            fprintf(sg_warnings_strm, "Bad file descriptor\n");
361        psp->os_err = ENODEV;
362        return -psp->os_err;
363    }
364    shp = handle_arr + index;
365    if (0 == shp->in_use) {
366        if (verbose)
367            fprintf(sg_warnings_strm, "File descriptor closed??\n");
368        psp->os_err = ENODEV;
369        return -psp->os_err;
370    }
371#ifdef SPTD
372    psp->swb.spt.Length = sizeof (SCSI_PASS_THROUGH_DIRECT);
373#else
374    if (psp->dxfer_len > (int)sizeof(psp->swb.ucDataBuf)) {
375        if (verbose)
376            fprintf(sg_warnings_strm, "dxfer_len (%d) too large (limit %d "
377                    "bytes)\n", psp->dxfer_len, sizeof(psp->swb.ucDataBuf));
378        psp->os_err = ENOMEM;
379        return -psp->os_err;
380
381    }
382    psp->swb.spt.Length = sizeof (SCSI_PASS_THROUGH);
383    psp->swb.spt.DataBufferOffset =
384                offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
385#endif
386    psp->swb.spt.PathId = shp->bus;
387    psp->swb.spt.TargetId = shp->target;
388    psp->swb.spt.Lun = shp->lun;
389    psp->swb.spt.TimeOutValue = time_secs;
390    psp->swb.spt.DataTransferLength = psp->dxfer_len;
391    if (verbose > 4) {
392        fprintf(stderr, " spt:: adapter: %s  Length=%d ScsiStatus=%d "
393                "PathId=%d TargetId=%d Lun=%d\n", shp->adapter,
394                (int)psp->swb.spt.Length,
395                (int)psp->swb.spt.ScsiStatus, (int)psp->swb.spt.PathId,
396                (int)psp->swb.spt.TargetId, (int)psp->swb.spt.Lun);
397        fprintf(stderr, "    CdbLength=%d SenseInfoLength=%d DataIn=%d "
398                "DataTransferLength=%lu\n",
399                (int)psp->swb.spt.CdbLength, (int)psp->swb.spt.SenseInfoLength,
400                (int)psp->swb.spt.DataIn, psp->swb.spt.DataTransferLength);
401#ifdef SPTD
402        fprintf(stderr, "    TimeOutValue=%lu SenseInfoOffset=%lu\n",
403                psp->swb.spt.TimeOutValue, psp->swb.spt.SenseInfoOffset);
404#else
405        fprintf(stderr, "    TimeOutValue=%lu DataBufferOffset=%lu "
406                "SenseInfoOffset=%lu\n", psp->swb.spt.TimeOutValue,
407                psp->swb.spt.DataBufferOffset, psp->swb.spt.SenseInfoOffset);
408#endif
409    }
410#ifdef SPTD
411    psp->swb.spt.DataBuffer = psp->dxferp;
412    status = DeviceIoControl(shp->fh, IOCTL_SCSI_PASS_THROUGH_DIRECT,
413                            &psp->swb,
414                            sizeof(psp->swb),
415                            &psp->swb,
416                            sizeof(psp->swb),
417                            &returned,
418                            NULL);
419#else
420    if ((psp->dxfer_len > 0) && (SCSI_IOCTL_DATA_OUT == psp->swb.spt.DataIn))
421        memcpy(psp->swb.ucDataBuf, psp->dxferp, psp->dxfer_len);
422    status = DeviceIoControl(shp->fh, IOCTL_SCSI_PASS_THROUGH,
423                            &psp->swb,
424                            sizeof(psp->swb),
425                            &psp->swb,
426                            sizeof(psp->swb),
427                            &returned,
428                            NULL);
429#endif
430    if (! status) {
431        psp->transport_err = GetLastError();
432        if (verbose)
433            fprintf(sg_warnings_strm, "Windows DeviceIoControl error=%d\n",
434                    psp->transport_err);
435        psp->os_err = EIO;
436        return 0;       /* let app find transport error */
437    }
438#ifndef SPTD
439    if ((psp->dxfer_len > 0) && (SCSI_IOCTL_DATA_IN == psp->swb.spt.DataIn))
440        memcpy(psp->dxferp, psp->swb.ucDataBuf, psp->dxfer_len);
441#endif
442
443    psp->scsi_status = psp->swb.spt.ScsiStatus;
444    if ((SAM_STAT_CHECK_CONDITION == psp->scsi_status) ||
445        (SAM_STAT_COMMAND_TERMINATED == psp->scsi_status))
446        memcpy(psp->sensep, psp->swb.ucSenseBuf, psp->sense_len);
447    else
448        psp->sense_len = 0;
449    psp->sense_resid = 0;
450    if ((psp->dxfer_len > 0) && (psp->swb.spt.DataTransferLength > 0))
451        psp->resid = psp->dxfer_len - psp->swb.spt.DataTransferLength;
452    else
453        psp->resid = 0;
454
455    return 0;
456}
457
458int get_scsi_pt_result_category(const struct sg_pt_base * vp)
459{
460    const struct sg_pt_win32_scsi * psp = &vp->impl;
461
462    if (psp->transport_err)     /* give transport error highest priority */
463        return SCSI_PT_RESULT_TRANSPORT_ERR;
464    else if (psp->os_err)
465        return SCSI_PT_RESULT_OS_ERR;
466    else if ((SAM_STAT_CHECK_CONDITION == psp->scsi_status) ||
467             (SAM_STAT_COMMAND_TERMINATED == psp->scsi_status))
468        return SCSI_PT_RESULT_SENSE;
469    else if (psp->scsi_status)
470        return SCSI_PT_RESULT_STATUS;
471    else
472        return SCSI_PT_RESULT_GOOD;
473}
474
475int get_scsi_pt_resid(const struct sg_pt_base * vp)
476{
477    const struct sg_pt_win32_scsi * psp = &vp->impl;
478
479    return psp->resid;
480}
481
482int get_scsi_pt_status_response(const struct sg_pt_base * vp)
483{
484    const struct sg_pt_win32_scsi * psp = &vp->impl;
485
486    return psp->scsi_status;
487}
488
489int get_scsi_pt_sense_len(const struct sg_pt_base * vp)
490{
491    const struct sg_pt_win32_scsi * psp = &vp->impl;
492    int len;
493
494    len = psp->sense_len - psp->sense_resid;
495    return (len > 0) ? len : 0;
496}
497
498int get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused)))
499{
500    // const struct sg_pt_freebsd_scsi * psp = &vp->impl;
501
502    return -1;
503}
504
505int get_scsi_pt_transport_err(const struct sg_pt_base * vp)
506{
507    const struct sg_pt_win32_scsi * psp = &vp->impl;
508
509    return psp->transport_err;
510}
511
512int get_scsi_pt_os_err(const struct sg_pt_base * vp)
513{
514    const struct sg_pt_win32_scsi * psp = &vp->impl;
515
516    return psp->os_err;
517}
518
519
520char * get_scsi_pt_transport_err_str(const struct sg_pt_base * vp,
521                                     int max_b_len, char * b)
522{
523    struct sg_pt_win32_scsi * psp = (struct sg_pt_win32_scsi *)&vp->impl;
524    LPVOID lpMsgBuf;
525    int k, num, ch;
526
527    if (max_b_len < 2) {
528        if (1 == max_b_len)
529            b[0] = '\0';
530        return b;
531    }
532    memset(b, 0, max_b_len);
533    FormatMessage(
534        FORMAT_MESSAGE_ALLOCATE_BUFFER |
535        FORMAT_MESSAGE_FROM_SYSTEM,
536        NULL,
537        psp->transport_err,
538        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
539        (LPTSTR) &lpMsgBuf,
540        0, NULL );
541    num = lstrlen((LPCTSTR)lpMsgBuf);
542    if (num < 1)
543        return b;
544    num = (num < max_b_len) ? num : (max_b_len - 1);
545    for (k = 0; k < num; ++k) {
546        ch = *((LPCTSTR)lpMsgBuf + k);
547        if ((ch >= 0x0) && (ch < 0x7f))
548            b[k] = ch & 0x7f;
549        else
550            b[k] = '?';
551    }
552    return b;
553}
554
555char * get_scsi_pt_os_err_str(const struct sg_pt_base * vp,
556                              int max_b_len, char * b)
557{
558    const struct sg_pt_win32_scsi * psp = &vp->impl;
559    const char * cp;
560
561    cp = safe_strerror(psp->os_err);
562    strncpy(b, cp, max_b_len);
563    if ((int)strlen(cp) >= max_b_len)
564        b[max_b_len - 1] = '\0';
565    return b;
566}
567