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 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/*
47 * This file shows the relationship between various SCSI device naming
48 * schemes in Windows OSes (Windows 200, 2003 and XP) as seen by
49 * The SCSI Pass Through (SPT) interface. N.B. ASPI32 is not used.
50 */
51
52#include <stdio.h>
53#include <stdlib.h>
54#include <stddef.h>
55#include <string.h>
56#include <errno.h>
57#include <ctype.h>
58#include <getopt.h>
59
60#include "sg_lib.h"
61#include "sg_pt_win32.h"
62
63#define MAX_SCSI_ELEMS 1024
64#define MAX_ADAPTER_NUM 64
65#define MAX_PHYSICALDRIVE_NUM 512
66#define MAX_CDROM_NUM 512
67#define MAX_TAPE_NUM 512
68#define MAX_HOLE_COUNT 8
69#define SCSI2_INQ_RESP_LEN 36
70#define DEF_TIMEOUT 20
71#define INQUIRY_CMD 0x12
72#define INQUIRY_CMDLEN 6
73
74struct w_scsi_elem {
75    char    in_use;
76    char    scsi_adapter_valid;
77    UCHAR   port_num;           /* <n> in '\\.\SCSI<n>:' adapter name */
78    UCHAR   bus;                /* also known as pathId */
79    UCHAR   target;
80    UCHAR   lun;
81    UCHAR   device_claimed;     /* class driver claimed this lu */
82    UCHAR   dubious_scsi;       /* set if inq_resp[4] is zero */
83    char    pdt;                /* peripheral device type (see SPC-4) */
84    char    volume_valid;
85    char    volume_multiple;    /* multiple partitions mapping to volumes */
86    UCHAR   volume_letter;      /* lowest 'C:' through to 'Z:' */
87    char    physicaldrive_valid;
88    char    cdrom_valid;
89    char    tape_valid;
90    int     physicaldrive_num;
91    int     cdrom_num;
92    int     tape_num;
93    unsigned char inq_resp[SCSI2_INQ_RESP_LEN];
94};
95
96static struct w_scsi_elem w_scsi_arr[MAX_SCSI_ELEMS];
97
98static int next_unused_scsi_elem = 0;
99static int next_elem_after_scsi_adapter_valid = 0;
100
101
102static char * get_err_str(DWORD err, int max_b_len, char * b)
103{
104    LPVOID lpMsgBuf;
105    int k, num, ch;
106
107    if (max_b_len < 2) {
108        if (1 == max_b_len)
109            b[0] = '\0';
110        return b;
111    }
112    memset(b, 0, max_b_len);
113    FormatMessage(
114        FORMAT_MESSAGE_ALLOCATE_BUFFER |
115        FORMAT_MESSAGE_FROM_SYSTEM,
116        NULL, err,
117        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
118        (LPTSTR) &lpMsgBuf, 0, NULL );
119    num = lstrlen((LPCTSTR)lpMsgBuf);
120    if (num < 1)
121        return b;
122    num = (num < max_b_len) ? num : (max_b_len - 1);
123    for (k = 0; k < num; ++k) {
124        ch = *((LPCTSTR)lpMsgBuf + k);
125        if ((ch >= 0x0) && (ch < 0x7f))
126            b[k] = ch & 0x7f;
127        else
128            b[k] = '?';
129    }
130    return b;
131}
132
133static int findElemIndex(UCHAR port_num, UCHAR bus, UCHAR target, UCHAR lun)
134{
135    int k;
136    struct w_scsi_elem * sep;
137
138    for (k = 0; k < next_unused_scsi_elem; ++k) {
139        sep = w_scsi_arr + k;
140        if ((port_num == sep->port_num) && (bus == sep->bus) &&
141            (target == sep->target) && (lun == sep->lun))
142            return k;
143#if 0
144        if (port_num < sep->port_num)
145            break;      /* assume port_num sorted ascending */
146#endif
147    }
148    return -1;
149}
150
151static BOOL fetchInquiry(HANDLE fh, unsigned char * resp, int max_resp_len,
152                         SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * afterCall,
153                         int verbose)
154{
155    BOOL success;
156    int len;
157    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptdw;
158    ULONG dummy;        /* also acts to align next array */
159    BYTE inqResp[SCSI2_INQ_RESP_LEN];
160    unsigned char inqCdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0,
161                                            SCSI2_INQ_RESP_LEN, 0};
162    DWORD err;
163    char b[256];
164
165    memset(&sptdw, 0, sizeof(sptdw));
166    memset(inqResp, 0, sizeof(inqResp));
167    sptdw.spt.Length = sizeof (SCSI_PASS_THROUGH_DIRECT);
168    sptdw.spt.CdbLength = sizeof(inqCdb);
169    sptdw.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
170    sptdw.spt.DataIn = SCSI_IOCTL_DATA_IN;
171    sptdw.spt.DataTransferLength = SCSI2_INQ_RESP_LEN;
172    sptdw.spt.TimeOutValue = DEF_TIMEOUT;
173    sptdw.spt.DataBuffer = inqResp;
174    sptdw.spt.SenseInfoOffset =
175                offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
176    memcpy(sptdw.spt.Cdb, inqCdb, sizeof(inqCdb));
177
178    success = DeviceIoControl(fh, IOCTL_SCSI_PASS_THROUGH_DIRECT,
179                              &sptdw, sizeof(sptdw),
180                              &sptdw, sizeof(sptdw),
181                              &dummy, NULL);
182    if (! success) {
183        if (verbose) {
184            err = GetLastError();
185            fprintf(stderr, "fetchInquiry: DeviceIoControl for INQUIRY, "
186                    "err=%lu\n\t%s", err, get_err_str(err, sizeof(b), b));
187        }
188        return success;
189    }
190    if (afterCall)
191        memcpy(afterCall, &sptdw, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
192    if (resp) {
193        len = (SCSI2_INQ_RESP_LEN > max_resp_len) ?
194              max_resp_len : SCSI2_INQ_RESP_LEN;
195        memcpy(resp, inqResp, len);
196    }
197    return success;
198}
199
200int sg_do_wscan(char letter, int verbose)
201{
202    int k, j, m, hole_count, index, matched;
203    DWORD err;
204    HANDLE fh;
205    ULONG dummy;
206    BOOL success;
207    BYTE bus;
208    PSCSI_ADAPTER_BUS_INFO  ai;
209    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptdw;
210    unsigned char inqResp[SCSI2_INQ_RESP_LEN];
211    char adapter_name[64];
212    char inqDataBuff[2048];
213    char b[256];
214    struct w_scsi_elem * sep;
215
216    memset(w_scsi_arr, 0, sizeof(w_scsi_arr));
217    hole_count = 0;
218    for (k = 0; k < MAX_ADAPTER_NUM; ++k) {
219        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\SCSI%d:", k);
220        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
221                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
222                        OPEN_EXISTING, 0, NULL);
223        if (fh != INVALID_HANDLE_VALUE) {
224            hole_count = 0;
225            success = DeviceIoControl(fh, IOCTL_SCSI_GET_INQUIRY_DATA,
226                                      NULL, 0, inqDataBuff, sizeof(inqDataBuff),
227                                      &dummy, FALSE);
228            if (success) {
229                PSCSI_BUS_DATA pbd;
230                PSCSI_INQUIRY_DATA pid;
231                int num_lus, off, len;
232
233                ai = (PSCSI_ADAPTER_BUS_INFO)inqDataBuff;
234                for (bus = 0; bus < ai->NumberOfBusses; bus++) {
235                    pbd = ai->BusData + bus;
236                    num_lus = pbd->NumberOfLogicalUnits;
237                    off = pbd->InquiryDataOffset;
238                    for (j = 0; j < num_lus; ++j) {
239                        if ((off < (int)sizeof(SCSI_ADAPTER_BUS_INFO)) ||
240                            (off > ((int)sizeof(inqDataBuff) -
241                                    (int)sizeof(SCSI_INQUIRY_DATA))))
242                            break;
243                        pid = (PSCSI_INQUIRY_DATA)(inqDataBuff + off);
244                        m = next_unused_scsi_elem++;
245                        if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
246                            fprintf(stderr, "Too many scsi devices (more "
247                                    "than %d)\n", MAX_SCSI_ELEMS);
248                            return SG_LIB_CAT_OTHER;
249                        }
250                        next_elem_after_scsi_adapter_valid =
251                                        next_unused_scsi_elem;
252                        sep = w_scsi_arr + m;
253                        sep->in_use = 1;
254                        sep->scsi_adapter_valid = 1;
255                        sep->port_num = k;
256                        sep->bus = pid->PathId;
257                        sep->target = pid->TargetId;
258                        sep->lun = pid->Lun;
259                        sep->device_claimed = pid->DeviceClaimed;
260                        len = pid->InquiryDataLength;
261                        len = (len > SCSI2_INQ_RESP_LEN) ?
262                              SCSI2_INQ_RESP_LEN : len;
263                        memcpy(sep->inq_resp, pid->InquiryData, len);
264                        sep->pdt = sep->inq_resp[0] & 0x3f;
265                        if (0 == sep->inq_resp[4])
266                            sep->dubious_scsi = 1;
267
268                        if (verbose > 1) {
269                            fprintf(stderr, "%s: PathId=%d TargetId=%d Lun=%d ",
270                                    adapter_name, pid->PathId, pid->TargetId, pid->Lun);
271                            fprintf(stderr, "  DeviceClaimed=%d\n", pid->DeviceClaimed);
272                            dStrHex((const char *)(pid->InquiryData), pid->InquiryDataLength, 0);
273                        }
274                        off = pid->NextInquiryDataOffset;
275                    }
276                }
277            } else {
278                err = GetLastError();
279                fprintf(stderr, "%s: IOCTL_SCSI_GET_INQUIRY_DATA failed "
280                        "err=%lu\n\t%s",
281                        adapter_name, err, get_err_str(err, sizeof(b), b));
282            }
283            CloseHandle(fh);
284        } else {
285            if (verbose > 2) {
286                err = GetLastError();
287                fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s",
288                        adapter_name, err, get_err_str(err, sizeof(b), b));
289            }
290            if (++hole_count >= MAX_HOLE_COUNT)
291                break;
292        }
293    }
294
295    for (k = 0; k < 24; ++k) {
296        matched = 0;
297        sep = NULL;
298        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\%c:", 'C' + k);
299        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
300                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
301                        OPEN_EXISTING, 0, NULL);
302        if (fh != INVALID_HANDLE_VALUE) {
303            success  = DeviceIoControl(fh, IOCTL_SCSI_GET_ADDRESS,
304                                       NULL, 0, inqDataBuff, sizeof(inqDataBuff),
305                                       &dummy, FALSE);
306            if (success) {
307                PSCSI_ADDRESS pa;
308
309                pa = (PSCSI_ADDRESS)inqDataBuff;
310                index = findElemIndex(pa->PortNumber, pa->PathId,
311                                      pa->TargetId, pa->Lun);
312                if (index >= 0) {
313                    sep = w_scsi_arr + index;
314                    matched = 1;
315                } else {
316                    m = next_unused_scsi_elem++;
317                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
318                        fprintf(stderr, "Too many scsi devices (more "
319                                "than %d)\n", MAX_SCSI_ELEMS);
320                        return SG_LIB_CAT_OTHER;
321                    }
322                    sep = w_scsi_arr + m;
323                    sep->in_use = 1;
324                    sep->port_num = pa->PortNumber;
325                    sep->bus = pa->PathId;
326                    sep->target = pa->TargetId;
327                    sep->lun = pa->Lun;
328                    sep->device_claimed = 1;
329                }
330                if (sep->volume_valid) {
331                    sep->volume_multiple = 1;
332                    if (('C' + k) == letter)
333                        sep->volume_letter = letter;
334                } else {
335                    sep->volume_valid = 1;
336                    sep->volume_letter = 'C' + k;
337                }
338                if (verbose > 1)
339                    fprintf(stderr, "%c: PortNum=%d PathId=%d TargetId=%d "
340                            "Lun=%d  index=%d\n", 'C' + k, pa->PortNumber,
341                            pa->PathId, pa->TargetId, pa->Lun, index);
342                if (matched) {
343                    CloseHandle(fh);
344                    continue;
345                }
346            } else {
347                if (verbose > 1) {
348                    err = GetLastError();
349                    fprintf(stderr, "%c: IOCTL_SCSI_GET_ADDRESS err=%lu\n\t"
350                            "%s", 'C' + k, err, get_err_str(err, sizeof(b), b));
351                }
352            }
353            if (fetchInquiry(fh, inqResp, sizeof(inqResp), &sptdw,
354                             verbose)) {
355                if (sptdw.spt.ScsiStatus) {
356                    if (verbose) {
357                        fprintf(stderr, "%c: INQUIRY failed:  ", 'C' + k);
358                        sg_print_scsi_status(sptdw.spt.ScsiStatus);
359                        sg_print_sense("    ", sptdw.ucSenseBuf,
360                                       sizeof(sptdw.ucSenseBuf), 0);
361                    }
362                    CloseHandle(fh);
363                    continue;
364                }
365                if (NULL == sep) {
366                    m = next_unused_scsi_elem++;
367                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
368                        fprintf(stderr, "Too many scsi devices (more "
369                                "than %d)\n", MAX_SCSI_ELEMS);
370                        return SG_LIB_CAT_OTHER;
371                    }
372                    sep = w_scsi_arr + m;
373                    sep->in_use = 1;
374                    sep->device_claimed = 1;
375                    sep->volume_valid = 1;
376                    sep->volume_letter = 'C' + k;
377                }
378                memcpy(sep->inq_resp, inqResp, sizeof(sep->inq_resp));
379                sep->pdt = sep->inq_resp[0] & 0x3f;
380                if (0 == sep->inq_resp[4])
381                    sep->dubious_scsi = 1;
382            }
383            CloseHandle(fh);
384        }
385    }
386
387    hole_count = 0;
388    for (k = 0; k < MAX_PHYSICALDRIVE_NUM; ++k) {
389        matched = 0;
390        sep = NULL;
391        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\PhysicalDrive%d", k);
392        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
393                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
394                        OPEN_EXISTING, 0, NULL);
395        if (fh != INVALID_HANDLE_VALUE) {
396            hole_count = 0;
397            success  = DeviceIoControl(fh, IOCTL_SCSI_GET_ADDRESS,
398                                       NULL, 0, inqDataBuff, sizeof(inqDataBuff),
399                                       &dummy, FALSE);
400            if (success) {
401                PSCSI_ADDRESS pa;
402
403                pa = (PSCSI_ADDRESS)inqDataBuff;
404                index = findElemIndex(pa->PortNumber, pa->PathId,
405                                      pa->TargetId, pa->Lun);
406                if (index >= 0) {
407                    sep = w_scsi_arr + index;
408                    matched = 1;
409                } else {
410                    m = next_unused_scsi_elem++;
411                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
412                        fprintf(stderr, "Too many scsi devices (more "
413                                "than %d)\n", MAX_SCSI_ELEMS);
414                        return SG_LIB_CAT_OTHER;
415                    }
416                    sep = w_scsi_arr + m;
417                    sep->in_use = 1;
418                    sep->port_num = pa->PortNumber;
419                    sep->bus = pa->PathId;
420                    sep->target = pa->TargetId;
421                    sep->lun = pa->Lun;
422                    sep->device_claimed = 1;
423                }
424                sep->physicaldrive_valid = 1;
425                sep->physicaldrive_num = k;
426                if (verbose > 1)
427                    fprintf(stderr, "PD%d: PortNum=%d PathId=%d TargetId=%d "
428                            "Lun=%d  index=%d\n", k, pa->PortNumber,
429                            pa->PathId, pa->TargetId, pa->Lun, index);
430                if (matched) {
431                    CloseHandle(fh);
432                    continue;
433                }
434            } else {
435                if (verbose > 1) {
436                    err = GetLastError();
437                    fprintf(stderr, "PD%d: IOCTL_SCSI_GET_ADDRESS err=%lu\n\t"
438                            "%s", k, err, get_err_str(err, sizeof(b), b));
439                }
440            }
441            if (fetchInquiry(fh, inqResp, sizeof(inqResp), &sptdw,
442                             verbose)) {
443                if (sptdw.spt.ScsiStatus) {
444                    if (verbose) {
445                        fprintf(stderr, "PD%d: INQUIRY failed:  ", k);
446                        sg_print_scsi_status(sptdw.spt.ScsiStatus);
447                        sg_print_sense("    ", sptdw.ucSenseBuf,
448                                       sizeof(sptdw.ucSenseBuf), 0);
449                    }
450                    CloseHandle(fh);
451                    continue;
452                }
453                if (NULL == sep) {
454                    m = next_unused_scsi_elem++;
455                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
456                        fprintf(stderr, "Too many scsi devices (more "
457                                "than %d)\n", MAX_SCSI_ELEMS);
458                        return SG_LIB_CAT_OTHER;
459                    }
460                    sep = w_scsi_arr + m;
461                    sep->in_use = 1;
462                    sep->device_claimed = 1;
463                    sep->physicaldrive_valid = 1;
464                    sep->physicaldrive_num = k;
465                }
466                memcpy(sep->inq_resp, inqResp, sizeof(sep->inq_resp));
467                sep->pdt = sep->inq_resp[0] & 0x3f;
468                if (0 == sep->inq_resp[4])
469                    sep->dubious_scsi = 1;
470            }
471            CloseHandle(fh);
472        } else {
473            if (verbose > 2) {
474                err = GetLastError();
475                fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s",
476                        adapter_name, err, get_err_str(err, sizeof(b), b));
477            }
478            if (++hole_count >= MAX_HOLE_COUNT)
479                break;
480        }
481    }
482
483    hole_count = 0;
484    for (k = 0; k < MAX_CDROM_NUM; ++k) {
485        matched = 0;
486        sep = NULL;
487        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\CDROM%d", k);
488        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
489                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
490                        OPEN_EXISTING, 0, NULL);
491        if (fh != INVALID_HANDLE_VALUE) {
492            hole_count = 0;
493            success  = DeviceIoControl(fh, IOCTL_SCSI_GET_ADDRESS,
494                                       NULL, 0, inqDataBuff, sizeof(inqDataBuff),
495                                       &dummy, FALSE);
496            if (success) {
497                PSCSI_ADDRESS pa;
498
499                pa = (PSCSI_ADDRESS)inqDataBuff;
500                index = findElemIndex(pa->PortNumber, pa->PathId,
501                                      pa->TargetId, pa->Lun);
502                if (index >= 0) {
503                    sep = w_scsi_arr + index;
504                    matched = 1;
505                } else {
506                    m = next_unused_scsi_elem++;
507                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
508                        fprintf(stderr, "Too many scsi devices (more "
509                                "than %d)\n", MAX_SCSI_ELEMS);
510                        return SG_LIB_CAT_OTHER;
511                    }
512                    sep = w_scsi_arr + m;
513                    sep->in_use = 1;
514                    sep->port_num = pa->PortNumber;
515                    sep->bus = pa->PathId;
516                    sep->target = pa->TargetId;
517                    sep->lun = pa->Lun;
518                    sep->device_claimed = 1;
519                }
520                sep->cdrom_valid = 1;
521                sep->cdrom_num = k;
522                if (verbose > 1)
523                    fprintf(stderr, "CDROM%d: PortNum=%d PathId=%d TargetId=%d "
524                            "Lun=%d  index=%d\n", k, pa->PortNumber,
525                            pa->PathId, pa->TargetId, pa->Lun, index);
526                if (matched) {
527                    CloseHandle(fh);
528                    continue;
529                }
530            } else {
531                if (verbose > 1) {
532                    err = GetLastError();
533                    fprintf(stderr, "CDROM%d: IOCTL_SCSI_GET_ADDRESS err=%lu\n\t"
534                            "%s", k, err, get_err_str(err, sizeof(b), b));
535                }
536            }
537            if (fetchInquiry(fh, inqResp, sizeof(inqResp), &sptdw,
538                             verbose)) {
539                if (sptdw.spt.ScsiStatus) {
540                    if (verbose) {
541                        fprintf(stderr, "CDROM%d: INQUIRY failed:  ", k);
542                        sg_print_scsi_status(sptdw.spt.ScsiStatus);
543                        sg_print_sense("    ", sptdw.ucSenseBuf,
544                                       sizeof(sptdw.ucSenseBuf), 0);
545                    }
546                    CloseHandle(fh);
547                    continue;
548                }
549                if (NULL == sep) {
550                    m = next_unused_scsi_elem++;
551                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
552                        fprintf(stderr, "Too many scsi devices (more "
553                                "than %d)\n", MAX_SCSI_ELEMS);
554                        return SG_LIB_CAT_OTHER;
555                    }
556                    sep = w_scsi_arr + m;
557                    sep->in_use = 1;
558                    sep->device_claimed = 1;
559                    sep->cdrom_valid = 1;
560                    sep->cdrom_num = k;
561                }
562                memcpy(sep->inq_resp, inqResp, sizeof(sep->inq_resp));
563                sep->pdt = sep->inq_resp[0] & 0x3f;
564                if (0 == sep->inq_resp[4])
565                    sep->dubious_scsi = 1;
566            }
567            CloseHandle(fh);
568        } else {
569            if (verbose > 3) {
570                err = GetLastError();
571                fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s",
572                        adapter_name, err, get_err_str(err, sizeof(b), b));
573            }
574            if (++hole_count >= MAX_HOLE_COUNT)
575                break;
576        }
577    }
578
579    hole_count = 0;
580    for (k = 0; k < MAX_TAPE_NUM; ++k) {
581        matched = 0;
582        sep = NULL;
583        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\TAPE%d", k);
584        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
585                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
586                        OPEN_EXISTING, 0, NULL);
587        if (fh != INVALID_HANDLE_VALUE) {
588            hole_count = 0;
589            success  = DeviceIoControl(fh, IOCTL_SCSI_GET_ADDRESS,
590                                       NULL, 0, inqDataBuff, sizeof(inqDataBuff),
591                                       &dummy, FALSE);
592            if (success) {
593                PSCSI_ADDRESS pa;
594
595                pa = (PSCSI_ADDRESS)inqDataBuff;
596                index = findElemIndex(pa->PortNumber, pa->PathId,
597                                      pa->TargetId, pa->Lun);
598                if (index >= 0) {
599                    sep = w_scsi_arr + index;
600                    matched = 1;
601                } else {
602                    m = next_unused_scsi_elem++;
603                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
604                        fprintf(stderr, "Too many scsi devices (more "
605                                "than %d)\n", MAX_SCSI_ELEMS);
606                        return SG_LIB_CAT_OTHER;
607                    }
608                    sep = w_scsi_arr + m;
609                    sep->in_use = 1;
610                    sep->port_num = pa->PortNumber;
611                    sep->bus = pa->PathId;
612                    sep->target = pa->TargetId;
613                    sep->lun = pa->Lun;
614                    sep->device_claimed = 1;
615                }
616                sep->tape_valid = 1;
617                sep->tape_num = k;
618                if (verbose > 1)
619                    fprintf(stderr, "TAPE%d: PortNum=%d PathId=%d TargetId=%d "
620                            "Lun=%d  index=%d\n", k, pa->PortNumber,
621                            pa->PathId, pa->TargetId, pa->Lun, index);
622                if (matched) {
623                    CloseHandle(fh);
624                    continue;
625                }
626            } else {
627                if (verbose > 1) {
628                    err = GetLastError();
629                    fprintf(stderr, "TAPE%d: IOCTL_SCSI_GET_ADDRESS "
630                            "err=%lu\n\t%s", k, err,
631                            get_err_str(err, sizeof(b), b));
632                }
633            }
634            if (fetchInquiry(fh, inqResp, sizeof(inqResp), &sptdw,
635                             verbose)) {
636                if (sptdw.spt.ScsiStatus) {
637                    if (verbose) {
638                        fprintf(stderr, "TAPE%d: INQUIRY failed:  ", k);
639                        sg_print_scsi_status(sptdw.spt.ScsiStatus);
640                        sg_print_sense("    ", sptdw.ucSenseBuf,
641                                       sizeof(sptdw.ucSenseBuf), 0);
642                    }
643                    CloseHandle(fh);
644                    continue;
645                }
646                if (NULL == sep) {
647                    m = next_unused_scsi_elem++;
648                    if (next_unused_scsi_elem > MAX_SCSI_ELEMS) {
649                        fprintf(stderr, "Too many scsi devices (more "
650                                "than %d)\n", MAX_SCSI_ELEMS);
651                        return SG_LIB_CAT_OTHER;
652                    }
653                    sep = w_scsi_arr + m;
654                    sep->in_use = 1;
655                    sep->device_claimed = 1;
656                    sep->tape_valid = 1;
657                    sep->tape_num = k;
658                }
659                memcpy(sep->inq_resp, inqResp, sizeof(sep->inq_resp));
660                sep->pdt = sep->inq_resp[0] & 0x3f;
661                if (0 == sep->inq_resp[4])
662                    sep->dubious_scsi = 1;
663            }
664            CloseHandle(fh);
665        } else {
666            if (verbose > 4) {
667                err = GetLastError();
668                fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s",
669                        adapter_name, err, get_err_str(err, sizeof(b), b));
670            }
671            if (++hole_count >= MAX_HOLE_COUNT)
672                break;
673        }
674    }
675
676    for (k = 0; k < MAX_SCSI_ELEMS; ++k) {
677        sep = w_scsi_arr + k;
678        if (0 == sep->in_use)
679            break;
680        if (sep->scsi_adapter_valid) {
681            snprintf(b, sizeof(b), "SCSI%d:%d,%d,%d ", sep->port_num,
682                     sep->bus, sep->target, sep->lun);
683            printf("%-18s", b);
684        } else
685            printf("                  ");
686        if (sep->volume_valid)
687            printf("%c: %c  ", sep->volume_letter,
688                   (sep->volume_multiple ? '+' : ' '));
689        else
690            printf("      ");
691        if (sep->physicaldrive_valid) {
692            snprintf(b, sizeof(b), "PD%d ", sep->physicaldrive_num);
693            printf("%-9s", b);
694        } else if (sep->cdrom_valid) {
695            snprintf(b, sizeof(b), "CDROM%d ", sep->cdrom_num);
696            printf("%-9s", b);
697        } else if (sep->tape_valid) {
698            snprintf(b, sizeof(b), "TAPE%d ", sep->tape_num);
699            printf("%-9s", b);
700        } else
701            printf("         ");
702
703        memcpy(b, sep->inq_resp + 8, SCSI2_INQ_RESP_LEN);
704        for (j = 0; j < 28; ++j) {
705            if ((b[j] < 0x20) || (b[j] > 0x7e))
706                b[j] = ' ';
707        }
708        b[28] = '\0';
709        printf("%-30s", b);
710        if (sep->dubious_scsi)
711            printf("*     ");
712        else if ((! sep->physicaldrive_valid) && (! sep->cdrom_valid) &&
713                 (! sep->tape_valid))
714            printf("pdt=%-2d", sep->pdt);
715        else
716            printf("      ");
717
718        printf("\n");
719    }
720    return 0;
721}
722