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