winaspi32.c 18.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
/*
 * Copyright 1997 Bruce Milner
 * Copyright 1998 Andreas Mohr
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "winbase.h"
#include "aspi.h"
#include "wnaspi32.h"
#include "winescsi.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(aspi);

/* FIXME!
 * 1) Residual byte length reporting not handled
 * 2) Make this code re-entrant for multithreading
 *    -- Added CriticalSection to OpenDevices function
 * 3) Only linux supported so far
 * 4) Leaves sg devices open. This may or may not be okay.  A better solution
 *    would be to close the file descriptors when the thread/process using
 *    them no longer needs them.
 */

#ifdef linux

static ASPI_DEVICE_INFO *ASPI_open_devices = NULL;
static CRITICAL_SECTION ASPI_CritSection = CRITICAL_SECTION_INIT("ASPI_CritSection");

#endif /* defined(linux) */


BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
{
#ifdef linux
	switch( fdwReason )
	{
	case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hInstDLL);
            SCSI_Init();
            break;
	case DLL_PROCESS_DETACH:
            DeleteCriticalSection( &ASPI_CritSection );
            break;
	}
#endif /* defined(linux) */
	return TRUE;
}


#ifdef linux

static int
ASPI_OpenDevice(SRB_ExecSCSICmd *prb)
{
    int	fd;
    DWORD	hc;
    ASPI_DEVICE_INFO *curr;

    /* search list of devices to see if we've opened it already.
     * There is not an explicit open/close in ASPI land, so hopefully
     * keeping a device open won't be a problem.
     */

    EnterCriticalSection(&ASPI_CritSection);
    for (curr = ASPI_open_devices; curr; curr = curr->next) {
	if (curr->hostId == prb->SRB_HaId &&
	    curr->target == prb->SRB_Target &&
	    curr->lun == prb->SRB_Lun) {
            LeaveCriticalSection(&ASPI_CritSection);
	    return curr->fd;
	}
    }
    LeaveCriticalSection(&ASPI_CritSection);

    if (prb->SRB_HaId > ASPI_GetNumControllers())
	return -1;

    hc = ASPI_GetHCforController( prb->SRB_HaId );
    fd = SCSI_OpenDevice( HIWORD(hc), LOWORD(hc), prb->SRB_Target, prb->SRB_Lun);

    if (fd == -1)
	return -1;

    /* device is now open */
    /* FIXME: Let users specify SCSI timeout in registry */
    SCSI_LinuxSetTimeout( fd, SCSI_DEFAULT_TIMEOUT );

    curr = HeapAlloc( GetProcessHeap(), 0, sizeof(ASPI_DEVICE_INFO) );
    curr->fd = fd;
    curr->hostId = prb->SRB_HaId;
    curr->target = prb->SRB_Target;
    curr->lun = prb->SRB_Lun;

    /* insert new record at beginning of open device list */
    EnterCriticalSection(&ASPI_CritSection);
    curr->next = ASPI_open_devices;
    ASPI_open_devices = curr;
    LeaveCriticalSection(&ASPI_CritSection);
    return fd;
}


static void
ASPI_DebugPrintCmd(SRB_ExecSCSICmd *prb)
{
  BYTE	cmd;
  int	i;
  BYTE *cdb;

  switch (prb->CDBByte[0]) {
  case CMD_INQUIRY:
    TRACE("INQUIRY {\n");
    TRACE("\tEVPD: %d\n", prb->CDBByte[1] & 1);
    TRACE("\tLUN: %d\n", (prb->CDBByte[1] & 0xc) >> 1);
    TRACE("\tPAGE CODE: %d\n", prb->CDBByte[2]);
    TRACE("\tALLOCATION LENGTH: %d\n", prb->CDBByte[4]);
    TRACE("\tCONTROL: %d\n", prb->CDBByte[5]);
    TRACE("}\n");
    break;
  case CMD_SCAN_SCAN:
    TRACE("Transfer Length: %d\n", prb->CDBByte[4]);
    break;
  }

  TRACE("Host Adapter: %d\n", prb->SRB_HaId);
  TRACE("Flags: %d\n", prb->SRB_Flags);
  if (TARGET_TO_HOST(prb)) {
    TRACE("\tData transfer: Target to host. Length checked.\n");
  }
  else if (HOST_TO_TARGET(prb)) {
    TRACE("\tData transfer: Host to target. Length checked.\n");
  }
  else if (NO_DATA_TRANSFERED(prb)) {
    TRACE("\tData transfer: none\n");
  }
  else {
    WARN("\tTransfer by scsi cmd. Length not checked.\n");
  }

  TRACE("\tResidual byte length reporting %s\n", prb->SRB_Flags & 0x4 ? "enabled" : "disabled");
  TRACE("\tLinking %s\n", prb->SRB_Flags & 0x2 ? "enabled" : "disabled");
  TRACE("\tPosting %s\n", prb->SRB_Flags & 0x1 ? "enabled" : "disabled");
  TRACE("Target: %d\n", prb->SRB_Target);
  TRACE("Lun: %d\n", prb->SRB_Lun);
  TRACE("BufLen: %ld\n", prb->SRB_BufLen);
  TRACE("SenseLen: %d\n", prb->SRB_SenseLen);
  TRACE("BufPtr: %p\n", prb->SRB_BufPointer);
  TRACE("CDB Length: %d\n", prb->SRB_CDBLen);
  TRACE("POST Proc: %lx\n", (DWORD) prb->SRB_PostProc);
  cdb = &prb->CDBByte[0];
  cmd = prb->CDBByte[0];
  if (TRACE_ON(aspi)) {
      TRACE("CDB buffer[");
      for (i = 0; i < prb->SRB_CDBLen; i++) {
          if (i != 0) TRACE(",");
          TRACE("%02x", *cdb++);
      }
      TRACE("]\n");
  }
}

static void
ASPI_PrintCDBArea(SRB_ExecSCSICmd *prb)
{
    if (TRACE_ON(aspi))
    {
	int i;
        TRACE("CDB[");
        for (i = 0; i < prb->SRB_CDBLen; i++) {
            if (i) TRACE(",");
            TRACE("%02x", prb->CDBByte[i]);
        }
        TRACE("]\n");
    }
}

static void
ASPI_PrintSenseArea(SRB_ExecSCSICmd *prb)
{
  int	i;
  BYTE	*rqbuf = prb->SenseArea;

  if (TRACE_ON(aspi))
  {
      TRACE("Request Sense reports:\n");
      if ((rqbuf[0]&0x7f)!=0x70) {
	      TRACE("\tInvalid sense header: 0x%02x instead of 0x70\n", rqbuf[0]&0x7f);
	      return;
      }
      TRACE("\tCurrent command read filemark: %s\n",(rqbuf[2]&0x80)?"yes":"no");
      TRACE("\tEarly warning passed: %s\n",(rqbuf[2]&0x40)?"yes":"no");
      TRACE("\tIncorrect blocklength: %s\n",(rqbuf[2]&0x20)?"yes":"no");
      TRACE("\tSense Key: %d\n",rqbuf[2]&0xf);
      if (rqbuf[0]&0x80)
	TRACE("\tResidual Length: %d\n",rqbuf[3]*0x1000000+rqbuf[4]*0x10000+rqbuf[5]*0x100+rqbuf[6]);
      TRACE("\tAdditional Sense Length: %d\n",rqbuf[7]);
      TRACE("\tAdditional Sense Code: %d\n",rqbuf[12]);
      TRACE("\tAdditional Sense Code Qualifier: %d\n",rqbuf[13]);
      if (rqbuf[15]&0x80) {
	TRACE("\tIllegal Param is in %s\n",(rqbuf[15]&0x40)?"the CDB":"the Data Out Phase");
	if (rqbuf[15]&0x8) {
	  TRACE("Pointer at %d, bit %d\n",rqbuf[16]*256+rqbuf[17],rqbuf[15]&0x7);
	}
      }
      TRACE("SenseArea[");
      for (i = 0; i < prb->SRB_SenseLen; i++) {
	if (i) TRACE(",");
	TRACE("%02x", *rqbuf++);
      }
      TRACE("]\n");
  }
}

static void
ASPI_DebugPrintResult(SRB_ExecSCSICmd *prb)
{

  TRACE("SRB_Status: %x\n", prb->SRB_Status);
  TRACE("SRB_HaStat: %x\n", prb->SRB_HaStat);
  TRACE("SRB_TargStat: %x\n", prb->SRB_TargStat);
  switch (prb->CDBByte[0]) {
  case CMD_INQUIRY:
    TRACE("Vendor: '%s'\n", prb->SRB_BufPointer + INQUIRY_VENDOR);
    break;
  case CMD_TEST_UNIT_READY:
    ASPI_PrintSenseArea(prb);
    break;
  }
}

/* Posting must be done in such a way that as soon as the SRB_Status is set
 * we don't touch the SRB anymore because it could possibly be freed
 * if the app is doing ASPI polling
 */
static DWORD
WNASPI32_DoPosting( SRB_ExecSCSICmd *lpPRB, DWORD status )
{
	void (*SRB_PostProc)() = lpPRB->SRB_PostProc;
	BYTE SRB_Flags = lpPRB->SRB_Flags;
	if( status == SS_PENDING )
	{
		WARN("Tried posting SS_PENDING\n");
		return SS_PENDING;
	}
	lpPRB->SRB_Status = status;
	/* lpPRB is NOT safe, it could be freed in another thread */

	if (SRB_PostProc)
	{
		if (SRB_Flags & 0x1)
		{
			TRACE("Post Routine (%lx) called\n", (DWORD) SRB_PostProc);
			/* Even though lpPRB could have been freed by
			 * the program.. that's unlikely if it planned
			 * to use it in the PostProc
			 */
			(*SRB_PostProc)(lpPRB);
		}
		else if (SRB_Flags & SRB_EVENT_NOTIFY) {
			TRACE("Setting event %p\n", (HANDLE)SRB_PostProc);
			SetEvent((HANDLE)SRB_PostProc);
		}
	}
	return SS_PENDING;
}

static WORD
ASPI_ExecScsiCmd(SRB_ExecSCSICmd *lpPRB)
{
  struct sg_header *sg_hd, *sg_reply_hdr;
  WORD ret;
  DWORD	status;
  int	in_len, out_len;
  int	error_code = 0;
  int	fd;
  DWORD SRB_Status;

  /* FIXME: hackmode */
#define MAKE_TARGET_TO_HOST(lpPRB) \
  	if (!TARGET_TO_HOST(lpPRB)) { \
	    WARN("program was not sending target_to_host for cmd %x (flags=%x),correcting.\n",lpPRB->CDBByte[0],lpPRB->SRB_Flags); \
	    lpPRB->SRB_Flags |= 0x08; \
	}
#define MAKE_HOST_TO_TARGET(lpPRB) \
  	if (!HOST_TO_TARGET(lpPRB)) { \
	    WARN("program was not sending host_to_target for cmd %x (flags=%x),correcting.\n",lpPRB->CDBByte[0],lpPRB->SRB_Flags); \
	    lpPRB->SRB_Flags |= 0x10; \
	}
  switch (lpPRB->CDBByte[0]) {
  case 0x12: /* INQUIRY */
  case 0x5a: /* MODE_SENSE_10 */
  case 0xa4: /* REPORT_KEY (DVD) MMC-2 */
  case 0xad: /* READ DVD STRUCTURE MMC-2 */
        MAKE_TARGET_TO_HOST(lpPRB)
	break;
  case 0xa3: /* SEND KEY (DVD) MMC-2 */
        MAKE_HOST_TO_TARGET(lpPRB)
	break;
  default:
	if ((((lpPRB->SRB_Flags & 0x18) == 0x00) ||
	     ((lpPRB->SRB_Flags & 0x18) == 0x18)
	    ) && lpPRB->SRB_BufLen
	) {
	    FIXME("command 0x%02x, no data transfer specified, but buflen is %ld!!!\n",lpPRB->CDBByte[0],lpPRB->SRB_BufLen);
	}
	break;
  }
  ASPI_DebugPrintCmd(lpPRB);
  if (lpPRB->SRB_HaId > ASPI_GetNumControllers()) {
      ERR("Failed: Wanted hostadapter %d, but we have only %d.\n",
	  lpPRB->SRB_HaId,ASPI_GetNumControllers()
      );
      return WNASPI32_DoPosting( lpPRB, SS_INVALID_HA );
  }
  fd = ASPI_OpenDevice(lpPRB);
  if (fd == -1) {
      return WNASPI32_DoPosting( lpPRB, SS_NO_DEVICE );
  }

  sg_hd = NULL;
  sg_reply_hdr = NULL;

  lpPRB->SRB_Status = SS_PENDING;

  if (!lpPRB->SRB_CDBLen) {
      ERR("Failed: lpPRB->SRB_CDBLen = 0.\n");
      return WNASPI32_DoPosting( lpPRB, SS_INVALID_SRB );
  }

  /* build up sg_header + scsi cmd */
  if (HOST_TO_TARGET(lpPRB)) {
    /* send header, command, and then data */
    in_len = SCSI_OFF + lpPRB->SRB_CDBLen + lpPRB->SRB_BufLen;
    sg_hd = (struct sg_header *) HeapAlloc(GetProcessHeap(), 0, in_len);
    memset(sg_hd, 0, SCSI_OFF);
    memcpy(sg_hd + 1, &lpPRB->CDBByte[0], lpPRB->SRB_CDBLen);
    if (lpPRB->SRB_BufLen) {
      memcpy(((BYTE *) sg_hd) + SCSI_OFF + lpPRB->SRB_CDBLen, lpPRB->SRB_BufPointer, lpPRB->SRB_BufLen);
    }
  }
  else {
    /* send header and command - no data */
    in_len = SCSI_OFF + lpPRB->SRB_CDBLen;
    sg_hd = (struct sg_header *) HeapAlloc(GetProcessHeap(), 0, in_len);
    memset(sg_hd, 0, SCSI_OFF);
    memcpy(sg_hd + 1, &lpPRB->CDBByte[0], lpPRB->SRB_CDBLen);
  }

  if (TARGET_TO_HOST(lpPRB)) {
    out_len = SCSI_OFF + lpPRB->SRB_BufLen;
    sg_reply_hdr = (struct sg_header *) HeapAlloc(GetProcessHeap(), 0, out_len);
    memset(sg_reply_hdr, 0, SCSI_OFF);
    sg_hd->reply_len = out_len;
  }
  else {
    out_len = SCSI_OFF;
    sg_reply_hdr = (struct sg_header *) HeapAlloc(GetProcessHeap(), 0, out_len);
    memset(sg_reply_hdr, 0, SCSI_OFF);
    sg_hd->reply_len = out_len;
  }

  SCSI_Fix_CMD_LEN(fd, lpPRB->CDBByte[0], lpPRB->SRB_CDBLen);

  if(!SCSI_LinuxDeviceIo( fd,
			  sg_hd, in_len,
			  sg_reply_hdr, out_len,
			  &status) )
  {
    goto error_exit;
  }

  if (sg_reply_hdr->result != 0) {
    error_code = sg_reply_hdr->result;
    WARN("reply header error (%d)\n", sg_reply_hdr->result);
    goto error_exit;
  }

  if (TARGET_TO_HOST(lpPRB) && lpPRB->SRB_BufLen) {
    memcpy(lpPRB->SRB_BufPointer, sg_reply_hdr + 1, lpPRB->SRB_BufLen);
  }

  /* copy in sense buffer to amount that is available in client */
  if (lpPRB->SRB_SenseLen) {
    int sense_len = lpPRB->SRB_SenseLen;
    if (lpPRB->SRB_SenseLen > 16)
      sense_len = 16;

    /* CDB is fixed in WNASPI32 */
    memcpy(lpPRB->SenseArea, &sg_reply_hdr->sense_buffer[0], sense_len);

    ASPI_PrintCDBArea(lpPRB);
    ASPI_PrintSenseArea(lpPRB);
  }

  SRB_Status = SS_COMP;
  lpPRB->SRB_HaStat = HASTAT_OK;
  lpPRB->SRB_TargStat = sg_reply_hdr->target_status << 1;

  HeapFree(GetProcessHeap(), 0, sg_reply_hdr);
  HeapFree(GetProcessHeap(), 0, sg_hd);

  /* FIXME: Should this be != 0 maybe? */
  if( lpPRB->SRB_TargStat == 2 ) {
    SRB_Status = SS_ERR;
    switch (lpPRB->CDBByte[0]) {
    case 0xa4: /* REPORT_KEY (DVD) MMC-2 */
    case 0xa3: /* SEND KEY (DVD) MMC-2 */
          SRB_Status = SS_COMP;
	  lpPRB->SRB_TargStat = 0;
	  FIXME("Program wants to do DVD Region switching, but fails (non compliant DVD drive). Ignoring....\n");
	  break;
    }
  }

  ASPI_DebugPrintResult(lpPRB);
  /* now do posting */
  ret = WNASPI32_DoPosting( lpPRB, SRB_Status );

  switch (lpPRB->CDBByte[0]) {
  case CMD_INQUIRY:
      if (SRB_Status == SS_COMP)
	  return SS_COMP; /* some junk expects ss_comp here. */
      /*FALLTHROUGH*/
  default:
      break;
  }

  /* In real WNASPI32 stuff really is always pending because ASPI does things
     in the background, but we are not doing that (yet) */

  return ret;

error_exit:
  SRB_Status = SS_ERR;
  if (error_code == EBUSY) {
      WNASPI32_DoPosting( lpPRB, SS_ASPI_IS_BUSY );
      TRACE("Device busy\n");
  } else
      FIXME("Failed\n");

  /* I'm not sure exactly error codes work here
   * We probably should set lpPRB->SRB_TargStat, SRB_HaStat ?
   */
  WARN("error_exit\n");
  HeapFree(GetProcessHeap(), 0, sg_reply_hdr);
  HeapFree(GetProcessHeap(), 0, sg_hd);
  WNASPI32_DoPosting( lpPRB, SRB_Status );
  return SS_PENDING;
}

#endif /* defined(linux) */


/*******************************************************************
 *     GetASPI32SupportInfo		[WNASPI32.1]
 *
 * Checks if the ASPI subsystem is initialized correctly.
 *
 * RETURNS
 *    HIWORD: 0.
 *    HIBYTE of LOWORD: status (SS_COMP or SS_FAILED_INIT)
 *    LOBYTE of LOWORD: # of host adapters.
 */
DWORD __cdecl GetASPI32SupportInfo(void)
{
    DWORD controllers = ASPI_GetNumControllers();

    if (!controllers)
	return SS_NO_ADAPTERS << 8;
    return (SS_COMP << 8) | controllers ;
}

/***********************************************************************
 *             SendASPI32Command (WNASPI32.2)
 */
DWORD __cdecl SendASPI32Command(LPSRB lpSRB)
{
#ifdef linux
  switch (lpSRB->common.SRB_Cmd) {
  case SC_HA_INQUIRY:
    lpSRB->inquiry.SRB_Status = SS_COMP;       /* completed successfully */
    lpSRB->inquiry.HA_Count = ASPI_GetNumControllers();
    lpSRB->inquiry.HA_SCSI_ID = 7;             /* not always ID 7 */
    strcpy(lpSRB->inquiry.HA_ManagerId, "ASPI for WIN32"); /* max 15 chars, don't change */
    strcpy(lpSRB->inquiry.HA_Identifier, "Wine host"); /* FIXME: return host adapter name */
    memset(lpSRB->inquiry.HA_Unique, 0, 16); /* default HA_Unique content */
    lpSRB->inquiry.HA_Unique[6] = 0x02; /* Maximum Transfer Length (128K, Byte> 4-7) */
    lpSRB->inquiry.HA_Unique[3] = 0x08; /* Maximum number of SCSI targets */
    FIXME("ASPI: Partially implemented SC_HA_INQUIRY for adapter %d.\n", lpSRB->inquiry.SRB_HaId);
    return SS_COMP;

  case SC_GET_DEV_TYPE: {
    /* FIXME: We should return SS_NO_DEVICE if the device is not configured */
    /* FIXME: We should return SS_INVALID_HA if HostAdapter!=0 */
    SRB		tmpsrb;
    char	inqbuf[200];
    DWORD	ret;

    memset(&tmpsrb,0,sizeof(tmpsrb));

    /* Copy header */
    memcpy(&tmpsrb.common,&(lpSRB->common),sizeof(tmpsrb.common));

    tmpsrb.cmd.SRB_Flags	|= 8; /* target to host */
    tmpsrb.cmd.SRB_Cmd 		= SC_EXEC_SCSI_CMD;
    tmpsrb.cmd.SRB_Target	= lpSRB->devtype.SRB_Target;
    tmpsrb.cmd.SRB_Lun		= lpSRB->devtype.SRB_Lun;
    tmpsrb.cmd.SRB_BufLen	= sizeof(inqbuf);
    tmpsrb.cmd.SRB_BufPointer	= inqbuf;
    tmpsrb.cmd.CDBByte[0]	= 0x12; /* INQUIRY  */
    				  /* FIXME: handle lun */
    tmpsrb.cmd.CDBByte[4]	= sizeof(inqbuf);
    tmpsrb.cmd.SRB_CDBLen	= 6;

    ret = ASPI_ExecScsiCmd(&tmpsrb.cmd);

    lpSRB->devtype.SRB_Status	= tmpsrb.cmd.SRB_Status;
    lpSRB->devtype.SRB_DeviceType = inqbuf[0]&0x1f;

    TRACE("returning devicetype %d for target %d\n",inqbuf[0]&0x1f,tmpsrb.cmd.SRB_Target);
    if (ret!=SS_PENDING) /* Any error is passed down directly */
	return ret;
    /* FIXME: knows that the command is finished already, pass final Status */
    return tmpsrb.cmd.SRB_Status;
  }
  case SC_EXEC_SCSI_CMD:
    return ASPI_ExecScsiCmd(&lpSRB->cmd);
  case SC_ABORT_SRB:
    FIXME("Not implemented SC_ABORT_SRB\n");
    break;
  case SC_RESET_DEV:
    FIXME("Not implemented SC_RESET_DEV\n");
    break;
  case SC_GET_DISK_INFO:
    /* here we have to find out the int13 / bios association.
     * We just say we do not have any.
     */
    FIXME("SC_GET_DISK_INFO always return 'int13 unassociated disk'.\n");
    lpSRB->diskinfo.SRB_DriveFlags = 0; /* disk is not int13 served */
    return SS_COMP;
  default:
    FIXME("Unknown command %d\n", lpSRB->common.SRB_Cmd);
  }
  return SS_INVALID_SRB;
#else
  return SS_INVALID_SRB;
#endif
}


/***********************************************************************
 *             GetASPI32DLLVersion   (WNASPI32.4)
 */
DWORD __cdecl GetASPI32DLLVersion(void)
{
#ifdef linux
	TRACE("Returning version 1\n");
        return (DWORD)1;
#else
	FIXME("Please add SCSI support for your operating system, returning 0\n");
        return (DWORD)0;
#endif
}

/***********************************************************************
 *             GetASPI32Buffer   (WNASPI32.8)
 * Supposed to return a DMA capable large SCSI buffer.
 * Our implementation does not use those at all, all buffer stuff is
 * done in the kernel SG device layer. So we just heapalloc the buffer.
 */
BOOL __cdecl GetASPI32Buffer(PASPI32BUFF pab)
{
    pab->AB_BufPointer = HeapAlloc(GetProcessHeap(),
	    	pab->AB_ZeroFill?HEAP_ZERO_MEMORY:0,
		pab->AB_BufLen
    );
    if (!pab->AB_BufPointer) return FALSE;
    return TRUE;
}

/***********************************************************************
 *             FreeASPI32Buffer   (WNASPI32.14)
 */
BOOL __cdecl FreeASPI32Buffer(PASPI32BUFF pab)
{
    HeapFree(GetProcessHeap(),0,pab->AB_BufPointer);
    return TRUE;
}

/***********************************************************************
 *             TranslateASPI32Address   (WNASPI32.7)
 */
BOOL __cdecl TranslateASPI32Address(LPDWORD pdwPath, LPDWORD pdwDEVNODE)
{
    FIXME("(%p, %p), stub !\n", pdwPath, pdwDEVNODE);
    return TRUE;
}