Import of all sources.

This commit is contained in:
waltje
2018-02-20 21:44:51 -05:00
parent 8485cbfe1a
commit 7f4ddcd70b
685 changed files with 301786 additions and 0 deletions

437
src/scsi/queue.h Normal file
View File

@@ -0,0 +1,437 @@
/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */
/*
* QEMU version: Copy from netbsd, removed debug code, removed some of
* the implementations. Left in singly-linked lists, lists, simple
* queues, and tail queues.
*/
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef QUEUE_H
#define QUEUE_H
/*
* This file defines four types of data structures: singly-linked lists,
* lists, simple queues, and tail queues.
*
* A singly-linked list is headed by a single forward pointer. The
* elements are singly linked for minimum space and pointer manipulation
* overhead at the expense of O(n) removal for arbitrary elements. New
* elements can be added to the list after an existing element or at the
* head of the list. Elements being removed from the head of the list
* should use the explicit macro for this purpose for optimum
* efficiency. A singly-linked list may only be traversed in the forward
* direction. Singly-linked lists are ideal for applications with large
* datasets and few or no removals or for implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
/*
* List definitions.
*/
#define QLIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define QLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define QLIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#define QLIST_INIT(head) do { \
(head)->lh_first = NULL; \
} while (/*CONSTCOND*/0)
#define QLIST_SWAP(dstlist, srclist, field) do { \
void *tmplist; \
tmplist = (srclist)->lh_first; \
(srclist)->lh_first = (dstlist)->lh_first; \
if ((srclist)->lh_first != NULL) { \
(srclist)->lh_first->field.le_prev = &(srclist)->lh_first; \
} \
(dstlist)->lh_first = tmplist; \
if ((dstlist)->lh_first != NULL) { \
(dstlist)->lh_first->field.le_prev = &(dstlist)->lh_first; \
} \
} while (/*CONSTCOND*/0)
#define QLIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (/*CONSTCOND*/0)
#define QLIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (/*CONSTCOND*/0)
#define QLIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (/*CONSTCOND*/0)
#define QLIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
} while (/*CONSTCOND*/0)
#define QLIST_FOREACH(var, head, field) \
for ((var) = ((head)->lh_first); \
(var); \
(var) = ((var)->field.le_next))
#define QLIST_FOREACH_SAFE(var, head, field, next_var) \
for ((var) = ((head)->lh_first); \
(var) && ((next_var) = ((var)->field.le_next), 1); \
(var) = (next_var))
/*
* List access methods.
*/
#define QLIST_EMPTY(head) ((head)->lh_first == NULL)
#define QLIST_FIRST(head) ((head)->lh_first)
#define QLIST_NEXT(elm, field) ((elm)->field.le_next)
/*
* Singly-linked List definitions.
*/
#define QSLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define QSLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define QSLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List functions.
*/
#define QSLIST_INIT(head) do { \
(head)->slh_first = NULL; \
} while (/*CONSTCOND*/0)
#define QSLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (/*CONSTCOND*/0)
#define QSLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (/*CONSTCOND*/0)
#define QSLIST_INSERT_HEAD_ATOMIC(head, elm, field) do { \
typeof(elm) save_sle_next; \
do { \
save_sle_next = (elm)->field.sle_next = (head)->slh_first; \
} while (atomic_cmpxchg(&(head)->slh_first, save_sle_next, (elm)) != \
save_sle_next); \
} while (/*CONSTCOND*/0)
#define QSLIST_MOVE_ATOMIC(dest, src) do { \
(dest)->slh_first = atomic_xchg(&(src)->slh_first, NULL); \
} while (/*CONSTCOND*/0)
#define QSLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (/*CONSTCOND*/0)
#define QSLIST_REMOVE_AFTER(slistelm, field) do { \
(slistelm)->field.sle_next = \
QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \
} while (/*CONSTCOND*/0)
#define QSLIST_FOREACH(var, head, field) \
for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
#define QSLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = QSLIST_FIRST((head)); \
(var) && ((tvar) = QSLIST_NEXT((var), field), 1); \
(var) = (tvar))
/*
* Singly-linked List access methods.
*/
#define QSLIST_EMPTY(head) ((head)->slh_first == NULL)
#define QSLIST_FIRST(head) ((head)->slh_first)
#define QSLIST_NEXT(elm, field) ((elm)->field.sle_next)
/*
* Simple queue definitions.
*/
#define QSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define QSIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define QSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue functions.
*/
#define QSIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\
(head)->sqh_last = &(head)->sqh_first; \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \
QSIMPLEQ_INIT(removed); \
if (((removed)->sqh_first = (head)->sqh_first) != NULL) { \
if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) { \
(head)->sqh_last = &(head)->sqh_first; \
} \
(removed)->sqh_last = &(elm)->field.sqe_next; \
(elm)->field.sqe_next = NULL; \
} \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \
if ((head)->sqh_first == (elm)) { \
QSIMPLEQ_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->sqh_first; \
while (curelm->field.sqe_next != (elm)) \
curelm = curelm->field.sqe_next; \
if ((curelm->field.sqe_next = \
curelm->field.sqe_next->field.sqe_next) == NULL) \
(head)->sqh_last = &(curelm)->field.sqe_next; \
} \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_FOREACH(var, head, field) \
for ((var) = ((head)->sqh_first); \
(var); \
(var) = ((var)->field.sqe_next))
#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \
for ((var) = ((head)->sqh_first); \
(var) && ((next = ((var)->field.sqe_next)), 1); \
(var) = (next))
#define QSIMPLEQ_CONCAT(head1, head2) do { \
if (!QSIMPLEQ_EMPTY((head2))) { \
*(head1)->sqh_last = (head2)->sqh_first; \
(head1)->sqh_last = (head2)->sqh_last; \
QSIMPLEQ_INIT((head2)); \
} \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_LAST(head, type, field) \
(QSIMPLEQ_EMPTY((head)) ? \
NULL : \
((struct type *)(void *) \
((char *)((head)->sqh_last) - offsetof(struct type, field))))
/*
* Simple queue access methods.
*/
#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
#define QSIMPLEQ_FIRST(head) ((head)->sqh_first)
#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
/*
* Tail queue definitions.
*/
#define Q_TAILQ_HEAD(name, type, qual) \
struct name { \
qual type *tqh_first; /* first element */ \
qual type *qual *tqh_last; /* addr of last next element */ \
}
#define QTAILQ_HEAD(name, type) Q_TAILQ_HEAD(name, struct type,)
#define QTAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define Q_TAILQ_ENTRY(type, qual) \
struct { \
qual type *tqe_next; /* next element */ \
qual type *qual *tqe_prev; /* address of previous next element */\
}
#define QTAILQ_ENTRY(type) Q_TAILQ_ENTRY(struct type,)
/*
* Tail queue functions.
*/
#define QTAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (/*CONSTCOND*/0)
#define QTAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
} while (/*CONSTCOND*/0)
#define QTAILQ_FOREACH(var, head, field) \
for ((var) = ((head)->tqh_first); \
(var); \
(var) = ((var)->field.tqe_next))
#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \
for ((var) = ((head)->tqh_first); \
(var) && ((next_var) = ((var)->field.tqe_next), 1); \
(var) = (next_var))
#define QTAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
(var); \
(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
/*
* Tail queue access methods.
*/
#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define QTAILQ_FIRST(head) ((head)->tqh_first)
#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define QTAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define QTAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#endif /* QEMU_SYS_QUEUE_H */

261
src/scsi/scsi.c Normal file
View File

@@ -0,0 +1,261 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Handling of the SCSI controllers.
*
* Version: @(#)scsi.c 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* TheCollector1995, <mariogplayer@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../emu.h"
#include "../mem.h"
#include "../rom.h"
#include "../timer.h"
#include "../device.h"
#include "../cdrom/cdrom.h"
#include "../zip.h"
#include "../disk/hdc.h"
#include "../plat.h"
#include "scsi.h"
#include "scsi_aha154x.h"
#include "scsi_buslogic.h"
#include "scsi_ncr5380.h"
#include "scsi_ncr53c810.h"
#ifdef WALTJE
# include "scsi_wd33c93.h"
#endif
#include "scsi_x54x.h"
scsi_device_t SCSIDevices[SCSI_ID_MAX][SCSI_LUN_MAX];
#if 0
uint8_t SCSIPhase = 0xff;
uint8_t SCSIStatus = SCSI_STATUS_OK;
#endif
char scsi_fn[SCSI_NUM][512];
uint16_t scsi_hd_location[SCSI_NUM];
int scsi_card_current = 0;
int scsi_card_last = 0;
uint32_t SCSI_BufferLength;
static volatile
mutex_t *scsiMutex;
typedef struct {
const char *name;
const char *internal_name;
device_t *device;
void (*reset)(void *p);
} SCSI_CARD;
static SCSI_CARD scsi_cards[] = {
{ "None", "none", NULL, NULL },
{ "[ISA] Adaptec AHA-1540B","aha1540b", &aha1540b_device, x54x_device_reset },
{ "[ISA] Adaptec AHA-1542C","aha1542c", &aha1542c_device, x54x_device_reset },
{ "[ISA] Adaptec AHA-1542CF","aha1542cf", &aha1542cf_device, x54x_device_reset },
{ "[ISA] BusLogic BT-542BH","bt542bh", &buslogic_device, BuslogicDeviceReset },
{ "[ISA] BusLogic BT-545S", "bt545s", &buslogic_545s_device,BuslogicDeviceReset },
{ "[ISA] Longshine LCS-6821N","lcs6821n", &scsi_lcs6821n_device,NULL },
{ "[ISA] Ranco RT1000B", "rt1000b", &scsi_rt1000b_device, NULL },
{ "[ISA] Trantor T130B", "t130b", &scsi_t130b_device, NULL },
{ "[ISA] Sumo SCSI-AT", "scsiat", &scsi_scsiat_device, NULL },
#ifdef WALTJE_SCSI
{ "[ISA] Generic WDC33C93", "wd33c93", &scsi_wd33c93_device, NULL },
#endif
{ "[MCA] Adaptec AHA-1640", "aha1640", &aha1640_device, x54x_device_reset },
{ "[MCA] BusLogic BT-640A", "bt640a", &buslogic_640a_device,BuslogicDeviceReset },
{ "[PCI] BusLogic BT-958D", "bt958d", &buslogic_pci_device, BuslogicDeviceReset },
{ "[PCI] NCR 53C810", "ncr53c810", &ncr53c810_pci_device,NULL },
{ "[VLB] BusLogic BT-445S", "bt445s", &buslogic_445s_device,BuslogicDeviceReset },
{ "", "", NULL, NULL },
};
int
scsi_card_available(int card)
{
if (scsi_cards[card].device)
return(device_available(scsi_cards[card].device));
return(1);
}
char *
scsi_card_getname(int card)
{
return((char *)scsi_cards[card].name);
}
device_t *
scsi_card_getdevice(int card)
{
return(scsi_cards[card].device);
}
int
scsi_card_has_config(int card)
{
if (! scsi_cards[card].device) return(0);
return(scsi_cards[card].device->config ? 1 : 0);
}
char *
scsi_card_get_internal_name(int card)
{
return((char *)scsi_cards[card].internal_name);
}
int
scsi_card_get_from_internal_name(char *s)
{
int c = 0;
while (strlen((char *)scsi_cards[c].internal_name)) {
if (! strcmp((char *)scsi_cards[c].internal_name, s))
return(c);
c++;
}
return(0);
}
void
scsi_mutex(uint8_t start)
{
if (start)
scsiMutex = thread_create_mutex(L"VARCem.SCSIMutex");
else
thread_close_mutex((mutex_t *) scsiMutex);
}
void
scsi_card_init(void)
{
int i, j;
pclog("Building SCSI hard disk map...\n");
build_scsi_hd_map();
pclog("Building SCSI CD-ROM map...\n");
build_scsi_cdrom_map();
pclog("Building SCSI ZIP map...\n");
build_scsi_zip_map();
for (i=0; i<SCSI_ID_MAX; i++) {
for (j=0; j<SCSI_LUN_MAX; j++) {
if (scsi_hard_disks[i][j] != 0xff) {
SCSIDevices[i][j].LunType = SCSI_DISK;
} else if (scsi_cdrom_drives[i][j] != 0xff) {
SCSIDevices[i][j].LunType = SCSI_CDROM;
} else if (scsi_zip_drives[i][j] != 0xff) {
SCSIDevices[i][j].LunType = SCSI_ZIP;
} else {
SCSIDevices[i][j].LunType = SCSI_NONE;
}
SCSIDevices[i][j].CmdBuffer = NULL;
}
}
if (scsi_cards[scsi_card_current].device)
device_add(scsi_cards[scsi_card_current].device);
scsi_card_last = scsi_card_current;
}
void
scsi_card_reset(void)
{
void *p = NULL;
if (scsi_cards[scsi_card_current].device) {
p = device_get_priv(scsi_cards[scsi_card_current].device);
if (p != NULL) {
if (scsi_cards[scsi_card_current].reset)
scsi_cards[scsi_card_current].reset(p);
}
}
}
/* Initialization function for the SCSI layer */
void
SCSIReset(uint8_t id, uint8_t lun)
{
uint8_t cdrom_id = scsi_cdrom_drives[id][lun];
uint8_t zip_id = scsi_zip_drives[id][lun];
uint8_t hdd_id = scsi_hard_disks[id][lun];
if (hdd_id != 0xff) {
scsi_hd_reset(hdd_id);
SCSIDevices[id][lun].LunType = SCSI_DISK;
} else {
if (cdrom_id != 0xff) {
cdrom_reset(cdrom_id);
SCSIDevices[id][lun].LunType = SCSI_CDROM;
} else if (zip_id != 0xff) {
zip_reset(zip_id);
SCSIDevices[id][lun].LunType = SCSI_ZIP;
} else {
SCSIDevices[id][lun].LunType = SCSI_NONE;
}
}
if (SCSIDevices[id][lun].CmdBuffer != NULL) {
free(SCSIDevices[id][lun].CmdBuffer);
SCSIDevices[id][lun].CmdBuffer = NULL;
}
}
void
scsi_mutex_wait(uint8_t wait)
{
if (wait)
thread_wait_mutex((mutex_t *) scsiMutex);
else
thread_release_mutex((mutex_t *) scsiMutex);
}

384
src/scsi/scsi.h Normal file
View File

@@ -0,0 +1,384 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* SCSI controller handler header.
*
* Version: @(#)scsi.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* TheCollector1995, <mariogplayer@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef EMU_SCSI_H
#define EMU_SCSI_H
#define SCSI_TIME (5 * 100 * (1 << TIMER_SHIFT))
/* Configuration. */
#define SCSI_ID_MAX 16 /* 16 on wide buses */
#define SCSI_LUN_MAX 8 /* always 8 */
/* SCSI commands. */
#define GPCMD_TEST_UNIT_READY 0x00
#define GPCMD_REZERO_UNIT 0x01
#define GPCMD_REQUEST_SENSE 0x03
#define GPCMD_FORMAT_UNIT 0x04
#define GPCMD_IOMEGA_SENSE 0x06
#define GPCMD_READ_6 0x08
#define GPCMD_WRITE_6 0x0a
#define GPCMD_SEEK_6 0x0b
#define GPCMD_IOMEGA_SET_PROTECTION_MODE 0x0c
#define GPCMD_IOMEGA_EJECT 0x0d /* ATAPI only? */
#define GPCMD_INQUIRY 0x12
#define GPCMD_VERIFY_6 0x13
#define GPCMD_MODE_SELECT_6 0x15
#define GPCMD_SCSI_RESERVE 0x16
#define GPCMD_SCSI_RELEASE 0x17
#define GPCMD_MODE_SENSE_6 0x1a
#define GPCMD_START_STOP_UNIT 0x1b
#define GPCMD_SEND_DIAGNOSTIC 0x1d
#define GPCMD_PREVENT_REMOVAL 0x1e
#define GPCMD_READ_CDROM_CAPACITY 0x25
#define GPCMD_READ_10 0x28
#define GPCMD_WRITE_10 0x2a
#define GPCMD_SEEK_10 0x2b
#define GPCMD_WRITE_AND_VERIFY_10 0x2e
#define GPCMD_VERIFY_10 0x2f
#define GPCMD_READ_BUFFER 0x3c
#define GPCMD_WRITE_SAME_10 0x41
#define GPCMD_READ_SUBCHANNEL 0x42
#define GPCMD_READ_TOC_PMA_ATIP 0x43
#define GPCMD_READ_HEADER 0x44
#define GPCMD_PLAY_AUDIO_10 0x45
#define GPCMD_GET_CONFIGURATION 0x46
#define GPCMD_PLAY_AUDIO_MSF 0x47
#define GPCMD_PLAY_AUDIO_TRACK_INDEX 0x48
#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
#define GPCMD_PAUSE_RESUME 0x4b
#define GPCMD_STOP_PLAY_SCAN 0x4e
#define GPCMD_READ_DISC_INFORMATION 0x51
#define GPCMD_READ_TRACK_INFORMATION 0x52
#define GPCMD_MODE_SELECT_10 0x55
#define GPCMD_MODE_SENSE_10 0x5a
#define GPCMD_PLAY_AUDIO_12 0xa5
#define GPCMD_READ_12 0xa8
#define GPCMD_WRITE_12 0xaa
#define GPCMD_READ_DVD_STRUCTURE 0xad /* For reading. */
#define GPCMD_WRITE_AND_VERIFY_12 0xae
#define GPCMD_VERIFY_12 0xaf
#define GPCMD_PLAY_CD_OLD 0xb4
#define GPCMD_READ_CD_OLD 0xb8
#define GPCMD_READ_CD_MSF 0xb9
#define GPCMD_SCAN 0xba
#define GPCMD_SET_SPEED 0xbb
#define GPCMD_PLAY_CD 0xbc
#define GPCMD_MECHANISM_STATUS 0xbd
#define GPCMD_READ_CD 0xbe
#define GPCMD_SEND_DVD_STRUCTURE 0xbf /* This is for writing only, irrelevant to PCem. */
#define GPCMD_PAUSE_RESUME_ALT 0xc2
#define GPCMD_SCAN_ALT 0xcd /* Should be equivalent to 0xba */
#define GPCMD_SET_SPEED_ALT 0xda /* Should be equivalent to 0xbb */
/* Mode page codes for mode sense/set */
#define GPMODE_R_W_ERROR_PAGE 0x01
#define GPMODE_CDROM_PAGE 0x0d
#define GPMODE_CDROM_AUDIO_PAGE 0x0e
#define GPMODE_CAPABILITIES_PAGE 0x2a
#define GPMODE_ALL_PAGES 0x3f
/* SCSI Status Codes */
#define SCSI_STATUS_OK 0
#define SCSI_STATUS_CHECK_CONDITION 2
/* SCSI Sense Keys */
#define SENSE_NONE 0
#define SENSE_NOT_READY 2
#define SENSE_ILLEGAL_REQUEST 5
#define SENSE_UNIT_ATTENTION 6
/* SCSI Additional Sense Codes */
#define ASC_AUDIO_PLAY_OPERATION 0x00
#define ASC_NOT_READY 0x04
#define ASC_ILLEGAL_OPCODE 0x20
#define ASC_LBA_OUT_OF_RANGE 0x21
#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
#define ASC_INV_LUN 0x25
#define ASC_INV_FIELD_IN_PARAMETER_LIST 0x26
#define ASC_WRITE_PROTECTED 0x27
#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28
#define ASC_CAPACITY_DATA_CHANGED 0x2A
#define ASC_INCOMPATIBLE_FORMAT 0x30
#define ASC_MEDIUM_NOT_PRESENT 0x3a
#define ASC_DATA_PHASE_ERROR 0x4b
#define ASC_ILLEGAL_MODE_FOR_THIS_TRACK 0x64
#define ASCQ_UNIT_IN_PROCESS_OF_BECOMING_READY 0x01
#define ASCQ_INITIALIZING_COMMAND_REQUIRED 0x02
#define ASCQ_CAPACITY_DATA_CHANGED 0x09
#define ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS 0x11
#define ASCQ_AUDIO_PLAY_OPERATION_PAUSED 0x12
#define ASCQ_AUDIO_PLAY_OPERATION_COMPLETED 0x13
/* Tell RISC OS that we have a 4x CD-ROM drive (600kb/sec data, 706kb/sec raw).
Not that it means anything */
#define CDROM_SPEED 706 /* 0x2C2 */
#define BUFFER_SIZE (256*1024)
#define RW_DELAY (TIMER_USEC * 500)
/* Some generally useful CD-ROM information */
#define CD_MINS 75 /* max. minutes per CD */
#define CD_SECS 60 /* seconds per minute */
#define CD_FRAMES 75 /* frames per second */
#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE)
#define CD_MAX_SECTORS (CD_MAX_BYTES / 512)
/* Event notification classes for GET EVENT STATUS NOTIFICATION */
#define GESN_NO_EVENTS 0
#define GESN_OPERATIONAL_CHANGE 1
#define GESN_POWER_MANAGEMENT 2
#define GESN_EXTERNAL_REQUEST 3
#define GESN_MEDIA 4
#define GESN_MULTIPLE_HOSTS 5
#define GESN_DEVICE_BUSY 6
/* Event codes for MEDIA event status notification */
#define MEC_NO_CHANGE 0
#define MEC_EJECT_REQUESTED 1
#define MEC_NEW_MEDIA 2
#define MEC_MEDIA_REMOVAL 3 /* only for media changers */
#define MEC_MEDIA_CHANGED 4 /* only for media changers */
#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */
#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */
#define MS_TRAY_OPEN 1
#define MS_MEDIA_PRESENT 2
/*
* The MMC values are not IDE specific and might need to be moved
* to a common header if they are also needed for the SCSI emulation
*/
/* Profile list from MMC-6 revision 1 table 91 */
#define MMC_PROFILE_NONE 0x0000
#define MMC_PROFILE_CD_ROM 0x0008
#define MMC_PROFILE_CD_R 0x0009
#define MMC_PROFILE_CD_RW 0x000A
#define MMC_PROFILE_DVD_ROM 0x0010
#define MMC_PROFILE_DVD_R_SR 0x0011
#define MMC_PROFILE_DVD_RAM 0x0012
#define MMC_PROFILE_DVD_RW_RO 0x0013
#define MMC_PROFILE_DVD_RW_SR 0x0014
#define MMC_PROFILE_DVD_R_DL_SR 0x0015
#define MMC_PROFILE_DVD_R_DL_JR 0x0016
#define MMC_PROFILE_DVD_RW_DL 0x0017
#define MMC_PROFILE_DVD_DDR 0x0018
#define MMC_PROFILE_DVD_PLUS_RW 0x001A
#define MMC_PROFILE_DVD_PLUS_R 0x001B
#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A
#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B
#define MMC_PROFILE_BD_ROM 0x0040
#define MMC_PROFILE_BD_R_SRM 0x0041
#define MMC_PROFILE_BD_R_RRM 0x0042
#define MMC_PROFILE_BD_RE 0x0043
#define MMC_PROFILE_HDDVD_ROM 0x0050
#define MMC_PROFILE_HDDVD_R 0x0051
#define MMC_PROFILE_HDDVD_RAM 0x0052
#define MMC_PROFILE_HDDVD_RW 0x0053
#define MMC_PROFILE_HDDVD_R_DL 0x0058
#define MMC_PROFILE_HDDVD_RW_DL 0x005A
#define MMC_PROFILE_INVALID 0xFFFF
#define SCSI_ONLY 32
#define ATAPI_ONLY 16
#define IMPLEMENTED 8
#define NONDATA 4
#define CHECK_READY 2
#define ALLOW_UA 1
extern uint8_t SCSICommandTable[0x100];
extern uint8_t mode_sense_pages[0x40];
extern int readcdmode;
/* Mode sense/select stuff. */
extern uint8_t mode_pages_in[256][256];
extern uint8_t page_flags[256];
extern uint8_t prefix_len;
extern uint8_t page_current;
#define PAGE_CHANGEABLE 1
#define PAGE_CHANGED 2
struct _scsisense_ {
uint8_t SenseBuffer[18];
uint8_t SenseLength;
uint8_t UnitAttention;
uint8_t SenseKey;
uint8_t Asc;
uint8_t Ascq;
} SCSISense;
extern int cd_status;
extern int prev_status;
enum {
SCSI_NONE = 0,
SCSI_DISK,
SCSI_CDROM,
SCSI_ZIP
};
#define MSFtoLBA(m,s,f) ((((m*60)+s)*75)+f)
#define MSG_COMMAND_COMPLETE 0x00
#define BUS_DBP 0x01
#define BUS_SEL 0x02
#define BUS_IO 0x04
#define BUS_CD 0x08
#define BUS_MSG 0x10
#define BUS_REQ 0x20
#define BUS_BSY 0x40
#define BUS_RST 0x80
#define BUS_ACK 0x200
#define BUS_ATN 0x200
#define BUS_ARB 0x8000
#define BUS_SETDATA(val) ((uint32_t)val << 16)
#define BUS_GETDATA(val) ((val >> 16) & 0xff)
#define BUS_DATAMASK 0xff0000
#define BUS_IDLE (1 << 31)
#define SCSI_PHASE_DATA_OUT 0
#define SCSI_PHASE_DATA_IN BUS_IO
#define SCSI_PHASE_COMMAND BUS_CD
#define SCSI_PHASE_STATUS (BUS_CD | BUS_IO)
#define SCSI_PHASE_MESSAGE_OUT (BUS_MSG | BUS_CD)
#define SCSI_PHASE_MESSAGE_IN (BUS_MSG | BUS_CD | BUS_IO)
typedef struct {
uint8_t *CmdBuffer;
int LunType;
int32_t BufferLength;
uint8_t Status;
uint8_t Phase;
} scsi_device_t;
extern scsi_device_t SCSIDevices[SCSI_ID_MAX][SCSI_LUN_MAX];
extern void SCSIReset(uint8_t id, uint8_t lun);
extern int cdrom_add_error_and_subchannel(uint8_t *b, int real_sector_type);
extern int cdrom_LBAtoMSF_accurate(void);
extern int mode_select_init(uint8_t command, uint16_t pl_length, uint8_t do_save);
extern int mode_select_terminate(int force);
extern int mode_select_write(uint8_t val);
extern int scsi_card_current;
extern int scsi_card_available(int card);
extern char *scsi_card_getname(int card);
#ifdef EMU_DEVICE_H
extern device_t *scsi_card_getdevice(int card);
#endif
extern int scsi_card_has_config(int card);
extern char *scsi_card_get_internal_name(int card);
extern int scsi_card_get_from_internal_name(char *s);
extern void scsi_mutex(uint8_t start);
extern void scsi_card_init(void);
extern void scsi_card_reset(void);
extern uint8_t scsi_hard_disks[16][8];
extern int scsi_hd_err_stat_to_scsi(uint8_t id);
extern int scsi_hd_phase_to_scsi(uint8_t id);
extern int find_hdc_for_scsi_id(uint8_t scsi_id, uint8_t scsi_lun);
extern void build_scsi_hd_map(void);
extern void scsi_hd_reset(uint8_t id);
extern void scsi_hd_request_sense_for_scsi(uint8_t id, uint8_t *buffer, uint8_t alloc_length);
extern void scsi_hd_command(uint8_t id, uint8_t *cdb);
extern void scsi_hd_callback(uint8_t id);
#pragma pack(push,1)
typedef struct {
uint8_t hi;
uint8_t mid;
uint8_t lo;
} addr24;
#pragma pack(pop)
#define ADDR_TO_U32(x) (((x).hi<<16)|((x).mid<<8)|((x).lo&0xFF))
#define U32_TO_ADDR(a,x) do {(a).hi=(x)>>16;(a).mid=(x)>>8;(a).lo=(x)&0xFF;}while(0)
/*
*
* Scatter/Gather Segment List Definitions
*
* Adapter limits
*/
#define MAX_SG_DESCRIPTORS 32 /* Always make the array 32 elements long, if less are used, that's not an issue. */
#pragma pack(push,1)
typedef struct {
uint32_t Segment;
uint32_t SegmentPointer;
} SGE32;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
addr24 Segment;
addr24 SegmentPointer;
} SGE;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
uint8_t pages[0x40][0x40];
} mode_sense_pages_t;
#pragma pack(pop)
#define MODE_SELECT_PHASE_IDLE 0
#define MODE_SELECT_PHASE_HEADER 1
#define MODE_SELECT_PHASE_BLOCK_DESC 2
#define MODE_SELECT_PHASE_PAGE_HEADER 3
#define MODE_SELECT_PHASE_PAGE 4
#endif /*EMU_SCSI_H*/
extern void scsi_mutex_wait(uint8_t wait);

1181
src/scsi/scsi_aha154x.c Normal file

File diff suppressed because it is too large Load Diff

49
src/scsi/scsi_aha154x.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the Adaptec 154x driver.
*
* Version: @(#)scsi_154x.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef SCSI_AHA154X_H
# define SCSI_AHA154X_H
extern device_t aha1540b_device;
extern device_t aha1542c_device;
extern device_t aha1542cf_device;
extern device_t aha1640_device;
extern void aha_device_reset(void *p);
#endif /*SCSI_AHA154X_H*/

389
src/scsi/scsi_bus.c Normal file
View File

@@ -0,0 +1,389 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* The generic SCSI bus operations handler.
*
* NOTES: For now ported from PCem with some modifications
* but at least it's a start.
*
* Version: @(#)scsi_bus.c 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* TheCollector1995, <mariogplayer@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include "../emu.h"
#include "scsi.h"
#include "scsi_device.h"
#define STATE_IDLE 0
#define STATE_COMMAND 1
#define STATE_COMMANDWAIT 2
#define STATE_DATAIN 3
#define STATE_DATAOUT 4
#define STATE_STATUS 5
#define STATE_MESSAGEIN 6
#define STATE_PHASESEL 7
#define SET_BUS_STATE(bus, state) bus->bus_out = (bus->bus_out & ~(SCSI_PHASE_MESSAGE_IN)) | (state & (SCSI_PHASE_MESSAGE_IN))
uint32_t SCSI_BufferLength;
#ifdef ENABLE_SCSI_BUS_LOG
int scsi_bus_do_log = ENABLE_SCSI_BUS_LOG;
#endif
static void
scsi_bus_log(const char *fmt, ...)
{
#ifdef ENABLE_SCSI_BUS_LOG
va_list ap;
if (scsi_bus_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
#endif
}
/* get the length of a SCSI command based on its command byte type */
static int get_cmd_len(int cbyte)
{
int len;
int group;
group = (cbyte>>5) & 7;
if (group == 0) len = 6;
if (group == 1 || group == 2) len = 10;
if (group == 5) len = 12;
// scsi_bus_log("Command group %d, length %d\n", group, len);
return(len);
}
static int
get_dev_id(uint8_t data)
{
int c;
for (c = 0; c < SCSI_ID_MAX; c++) {
if (data & (1 << c)) return(c);
}
return(-1);
}
int
scsi_bus_update(scsi_bus_t *bus, int bus_assert)
{
scsi_device_t *dev;
uint8_t lun = 0;
if (bus_assert & BUS_ARB)
bus->state = STATE_IDLE;
switch (bus->state) {
case STATE_IDLE:
scsi_bus_log("State Idle\n");
bus->clear_req = bus->change_state_delay = bus->new_req_delay = 0;
if ((bus_assert & BUS_SEL) && !(bus_assert & BUS_BSY)) {
uint8_t sel_data = BUS_GETDATA(bus_assert);
bus->dev_id = get_dev_id(sel_data);
if ((bus->dev_id != -1) && scsi_device_present(bus->dev_id, 0)) {
bus->bus_out |= BUS_BSY;
bus->state = STATE_PHASESEL;
}
//scsi_bus_log("Device id %i\n", bus->dev_id);
break;
}
break;
case STATE_PHASESEL:
scsi_bus_log("State Phase Sel\n");
if (! (bus_assert & BUS_SEL)) {
if (! (bus_assert & BUS_ATN)) {
if ((bus->dev_id != -1) &&
scsi_device_present(bus->dev_id, 0)) {
bus->state = STATE_COMMAND;
bus->bus_out = BUS_BSY | BUS_REQ;
bus->command_pos = 0;
SET_BUS_STATE(bus, SCSI_PHASE_COMMAND);
} else {
bus->state = STATE_IDLE;
bus->bus_out = 0;
}
} else
fatal("dropped sel %x\n", bus_assert & BUS_ATN);
}
break;
case STATE_COMMAND:
if ((bus_assert & BUS_ACK) && !(bus->bus_in & BUS_ACK)) {
scsi_bus_log("State Command\n");
bus->command[bus->command_pos++] = BUS_GETDATA(bus_assert);
bus->clear_req = 3;
bus->new_state = bus->bus_out & SCSI_PHASE_MESSAGE_IN;
bus->bus_out &= ~BUS_REQ;
if (get_cmd_len(bus->command[0]) == bus->command_pos) {
lun = (bus->command[1] >> 5) & 7;
bus->data_pos = 0;
dev = &SCSIDevices[bus->dev_id][lun];
scsi_bus_log("Command 0x%02X\n", bus->command[0]);
dev->BufferLength = -1;
scsi_device_command_phase0(bus->dev_id, lun,
get_cmd_len(bus->command[0]),
bus->command);
scsi_bus_log("(%02X:%02X): Command %02X: Buffer Length %i, SCSI Phase %02X\n", bus->dev_id, lun, bus->command[0], dev->BufferLength, dev->Phase);
if ((dev->Phase == SCSI_PHASE_DATA_IN) ||
(dev->Phase == SCSI_PHASE_DATA_OUT)) {
scsi_bus_log("dev->CmdBuffer = %08X\n", dev->CmdBuffer);
dev->CmdBuffer = (uint8_t *) malloc(dev->BufferLength);
scsi_bus_log("dev->CmdBuffer = %08X\n", dev->CmdBuffer);
}
if (dev->Phase == SCSI_PHASE_DATA_OUT) {
/* Write direction commands have delayed execution - only execute them after the bus has gotten all the data from the host. */
scsi_bus_log("Next state is data out\n");
bus->state = STATE_COMMANDWAIT;
bus->clear_req = 0;
} else {
/* Other command - execute immediately. */
bus->new_state = dev->Phase;
if (dev->Phase == SCSI_PHASE_DATA_IN) {
scsi_device_command_phase1(bus->dev_id, lun);
}
bus->change_state_delay = 4;
}
}
}
break;
case STATE_COMMANDWAIT:
bus->new_state = SCSI_PHASE_DATA_OUT;
bus->change_state_delay = 4;
bus->clear_req = 4;
break;
case STATE_DATAIN:
if ((bus_assert & BUS_ACK) && !(bus->bus_in & BUS_ACK)) {
scsi_bus_log("State Data In\n");
/* This seems to be read, so we first execute the command, then we return the bytes to the host. */
lun = (bus->command[1] >> 5) & 7;
dev = &SCSIDevices[bus->dev_id][lun];
if (bus->data_pos >= SCSIDevices[bus->dev_id][lun].BufferLength) {
free(dev->CmdBuffer);
dev->CmdBuffer = NULL;
bus->bus_out &= ~BUS_REQ;
bus->new_state = SCSI_PHASE_STATUS;
bus->change_state_delay = 4;
bus->new_req_delay = 8;
} else {
uint8_t val = dev->CmdBuffer[bus->data_pos++];
bus->bus_out = (bus->bus_out & ~BUS_DATAMASK) | BUS_SETDATA(val) | BUS_DBP | BUS_REQ;
bus->clear_req = 3;
bus->bus_out &= ~BUS_REQ;
bus->new_state = SCSI_PHASE_DATA_IN;
}
}
break;
case STATE_DATAOUT:
if ((bus_assert & BUS_ACK) && !(bus->bus_in & BUS_ACK)) {
scsi_bus_log("State Data Out\n");
lun = (bus->command[1] >> 5) & 7;
dev = &SCSIDevices[bus->dev_id][lun];
/* This is write, so first get the data from the host, then execute the last phase of the command. */
dev->CmdBuffer[bus->data_pos++] = BUS_GETDATA(bus_assert);
if (bus->data_pos >= SCSIDevices[bus->dev_id][lun].BufferLength) {
/* scsi_bus_log("%04X bytes written (%02X %02X)\n", bus->data_pos, bus->command[0], bus->command[1]); */
scsi_bus_log("Actually executing write command\n");
scsi_device_command_phase1(bus->dev_id, lun);
free(dev->CmdBuffer);
dev->CmdBuffer = NULL;
bus->bus_out &= ~BUS_REQ;
bus->new_state = SCSI_PHASE_STATUS;
bus->change_state_delay = 4;
bus->new_req_delay = 8;
} else {
bus->bus_out |= BUS_REQ;
}
}
break;
case STATE_STATUS:
scsi_bus_log("State Status\n");
if ((bus_assert & BUS_ACK) && !(bus->bus_in & BUS_ACK)) {
/* scsi_bus_log("Preparing for message in\n"); */
bus->bus_out &= ~BUS_REQ;
bus->new_state = SCSI_PHASE_MESSAGE_IN;
bus->change_state_delay = 4;
bus->new_req_delay = 8;
}
break;
case STATE_MESSAGEIN:
scsi_bus_log("State Message In\n");
if ((bus_assert & BUS_ACK) && !(bus->bus_in & BUS_ACK)) {
bus->bus_out &= ~BUS_REQ;
bus->new_state = BUS_IDLE;
bus->change_state_delay = 4;
}
break;
}
bus->bus_in = bus_assert;
return(bus->bus_out | bus->bus_in);
}
int
scsi_bus_read(scsi_bus_t *bus)
{
scsi_device_t *dev;
uint8_t lun = 0;
if (bus->clear_req) {
bus->clear_req--;
if (!bus->clear_req) {
scsi_bus_log("Clear REQ\n");
SET_BUS_STATE(bus, bus->new_state);
bus->bus_out |= BUS_REQ;
}
}
if (bus->change_state_delay) {
bus->change_state_delay--;
if (!bus->change_state_delay) {
uint8_t val;
scsi_bus_log("Change state delay\n");
SET_BUS_STATE(bus, bus->new_state);
switch (bus->bus_out & SCSI_PHASE_MESSAGE_IN) {
case SCSI_PHASE_DATA_IN:
lun = (bus->command[1] >> 5) & 7;
dev = &SCSIDevices[bus->dev_id][lun];
scsi_bus_log("Phase data in\n");
bus->state = STATE_DATAIN;
val = dev->CmdBuffer[bus->data_pos++];
bus->bus_out = (bus->bus_out & ~BUS_DATAMASK) | BUS_SETDATA(val) | BUS_DBP;
break;
case SCSI_PHASE_DATA_OUT:
scsi_bus_log("Phase data out\n");
if (bus->new_state & BUS_IDLE) {
bus->state = STATE_IDLE;
bus->bus_out &= ~BUS_BSY;
} else {
bus->state = STATE_DATAOUT;
}
break;
case SCSI_PHASE_STATUS:
lun = (bus->command[1] >> 5) & 7;
dev = &SCSIDevices[bus->dev_id][lun];
scsi_bus_log("Phase status\n");
bus->state = STATE_STATUS;
bus->bus_out |= BUS_REQ;
bus->bus_out = (bus->bus_out & ~BUS_DATAMASK) | BUS_SETDATA(dev->Status) | BUS_DBP;
/* scsi_bus_log("SCSI Status (command %02X): %02X (%08X)\n", bus->command[0], dev->Status, bus->bus_out); */
break;
case SCSI_PHASE_MESSAGE_IN:
scsi_bus_log("Phase message in\n");
/* scsi_bus_log("Message in\n"); */
bus->state = STATE_MESSAGEIN;
bus->bus_out = (bus->bus_out & ~BUS_DATAMASK) | BUS_SETDATA(0) | BUS_DBP;
break;
default:
fatal("change_state_delay bad state %x\n", bus->bus_out);
}
}
}
if (bus->new_req_delay) {
bus->new_req_delay--;
if (!bus->new_req_delay) {
bus->bus_out |= BUS_REQ;
}
}
return(bus->bus_out);
}
int
scsi_bus_match(scsi_bus_t *bus, int bus_assert)
{
return((bus_assert & (BUS_CD | BUS_IO | BUS_MSG)) ==
(bus->bus_out & (BUS_CD | BUS_IO | BUS_MSG)));
}

1829
src/scsi/scsi_buslogic.c Normal file

File diff suppressed because it is too large Load Diff

49
src/scsi/scsi_buslogic.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* Emulation of BusLogic BT-542B ISA and BT-958D PCI SCSI
* controllers.
*
* Version: @(#)scsi_buslogic.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef SCSI_BUSLOGIC_H
# define SCSI_BUSLOGIC_H
extern device_t buslogic_device;
extern device_t buslogic_545s_device;
extern device_t buslogic_640a_device;
extern device_t buslogic_445s_device;
extern device_t buslogic_pci_device;
extern void BuslogicDeviceReset(void *p);
#endif /*SCSI_BUSLOGIC_H*/

387
src/scsi/scsi_device.c Normal file
View File

@@ -0,0 +1,387 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* The generic SCSI device command handler.
*
* Version: @(#)scsi_device.c 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include "../emu.h"
#include "../device.h"
#include "../cdrom/cdrom.h"
#include "../zip.h"
#include "../disk/hdd.h"
#include "scsi.h"
#include "scsi_disk.h"
static uint8_t scsi_null_device_sense[14] = { 0x70,0,SENSE_ILLEGAL_REQUEST,0,0,0,0,0,0,0,0,0,ASC_INV_LUN,0 };
static uint8_t scsi_device_target_command(int lun_type, uint8_t id, uint8_t *cdb)
{
if (lun_type == SCSI_DISK)
{
scsi_hd_command(id, cdb);
return scsi_hd_err_stat_to_scsi(id);
}
else if (lun_type == SCSI_CDROM)
{
cdrom_command(id, cdb);
return cdrom_CDROM_PHASE_to_scsi(id);
}
else if (lun_type == SCSI_ZIP)
{
zip_command(id, cdb);
return zip_ZIP_PHASE_to_scsi(id);
}
else
{
return SCSI_STATUS_CHECK_CONDITION;
}
}
static void scsi_device_target_phase_callback(int lun_type, uint8_t id)
{
if (lun_type == SCSI_DISK)
{
scsi_hd_callback(id);
}
else if (lun_type == SCSI_CDROM)
{
cdrom_phase_callback(id);
}
else if (lun_type == SCSI_ZIP)
{
zip_phase_callback(id);
}
else
{
return;
}
}
static int scsi_device_target_err_stat_to_scsi(int lun_type, uint8_t id)
{
if (lun_type == SCSI_DISK)
{
return scsi_hd_err_stat_to_scsi(id);
}
else if (lun_type == SCSI_CDROM)
{
return cdrom_CDROM_PHASE_to_scsi(id);
}
else if (lun_type == SCSI_ZIP)
{
return zip_ZIP_PHASE_to_scsi(id);
}
else
{
return SCSI_STATUS_CHECK_CONDITION;
}
}
static void scsi_device_target_save_cdb_byte(int lun_type, uint8_t id, uint8_t cdb_byte)
{
if (lun_type == SCSI_DISK)
{
shdc[id].request_length = cdb_byte;
}
else if (lun_type == SCSI_CDROM)
{
cdrom[id].request_length = cdb_byte;
}
else if (lun_type == SCSI_ZIP)
{
zip[id].request_length = cdb_byte;
}
else
{
return;
}
}
uint8_t *scsi_device_sense(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_DISK:
id = scsi_hard_disks[scsi_id][scsi_lun];
return shdc[id].sense;
break;
case SCSI_CDROM:
id = scsi_cdrom_drives[scsi_id][scsi_lun];
return cdrom[id].sense;
break;
case SCSI_ZIP:
id = scsi_zip_drives[scsi_id][scsi_lun];
return zip[id].sense;
break;
default:
return scsi_null_device_sense;
break;
}
}
void scsi_device_request_sense(uint8_t scsi_id, uint8_t scsi_lun, uint8_t *buffer, uint8_t alloc_length)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_DISK:
id = scsi_hard_disks[scsi_id][scsi_lun];
scsi_hd_request_sense_for_scsi(id, buffer, alloc_length);
break;
case SCSI_CDROM:
id = scsi_cdrom_drives[scsi_id][scsi_lun];
cdrom_request_sense_for_scsi(id, buffer, alloc_length);
break;
case SCSI_ZIP:
id = scsi_zip_drives[scsi_id][scsi_lun];
zip_request_sense_for_scsi(id, buffer, alloc_length);
break;
default:
memcpy(buffer, scsi_null_device_sense, alloc_length);
break;
}
}
void scsi_device_type_data(uint8_t scsi_id, uint8_t scsi_lun, uint8_t *type, uint8_t *rmb)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_DISK:
id = scsi_hard_disks[scsi_id][scsi_lun];
*type = 0x00;
*rmb = (hdd[id].bus == HDD_BUS_SCSI_REMOVABLE) ? 0x80 : 0x00;
break;
case SCSI_CDROM:
*type = 0x05;
*rmb = 0x80;
break;
case SCSI_ZIP:
*type = 0x00;
*rmb = 0x80;
break;
default:
*type = *rmb = 0xFF;
break;
}
}
int scsi_device_read_capacity(uint8_t scsi_id, uint8_t scsi_lun, uint8_t *cdb, uint8_t *buffer, uint32_t *len)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_DISK:
id = scsi_hard_disks[scsi_id][scsi_lun];
return scsi_hd_read_capacity(id, cdb, buffer, len);
case SCSI_CDROM:
id = scsi_cdrom_drives[scsi_id][scsi_lun];
return cdrom_read_capacity(id, cdb, buffer, len);
case SCSI_ZIP:
id = scsi_zip_drives[scsi_id][scsi_lun];
return zip_read_capacity(id, cdb, buffer, len);
default:
return 0;
}
}
int scsi_device_present(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
switch (lun_type)
{
case SCSI_NONE:
return 0;
default:
return 1;
}
}
int scsi_device_valid(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_DISK:
id = scsi_hard_disks[scsi_id][scsi_lun];
break;
case SCSI_CDROM:
id = scsi_cdrom_drives[scsi_id][scsi_lun];
break;
case SCSI_ZIP:
id = scsi_zip_drives[scsi_id][scsi_lun];
break;
default:
id = 0;
break;
}
return (id == 0xFF) ? 0 : 1;
}
int scsi_device_cdb_length(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_CDROM:
id = scsi_cdrom_drives[scsi_id][scsi_lun];
return cdrom[id].cdb_len;
case SCSI_ZIP:
id = scsi_zip_drives[scsi_id][scsi_lun];
return zip[id].cdb_len;
default:
return 12;
}
}
void scsi_device_command_phase0(uint8_t scsi_id, uint8_t scsi_lun, int cdb_len, uint8_t *cdb)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_DISK:
id = scsi_hard_disks[scsi_id][scsi_lun];
break;
case SCSI_CDROM:
id = scsi_cdrom_drives[scsi_id][scsi_lun];
break;
case SCSI_ZIP:
id = scsi_zip_drives[scsi_id][scsi_lun];
break;
default:
id = 0;
SCSIDevices[scsi_id][scsi_lun].Phase = SCSI_PHASE_STATUS;
SCSIDevices[scsi_id][scsi_lun].Status = SCSI_STATUS_CHECK_CONDITION;
return;
}
/*
* Since that field in the target struct is never used when
* the bus type is SCSI, let's use it for this scope.
*/
scsi_device_target_save_cdb_byte(lun_type, id, cdb[1]);
if (cdb_len != 12) {
/*
* Make sure the LUN field of the temporary CDB is always 0,
* otherwise Daemon Tools drives will misbehave when a command
* is passed through to them.
*/
cdb[1] &= 0x1f;
}
/* Finally, execute the SCSI command immediately and get the transfer length. */
SCSIDevices[scsi_id][scsi_lun].Phase = SCSI_PHASE_COMMAND;
SCSIDevices[scsi_id][scsi_lun].Status = scsi_device_target_command(lun_type, id, cdb);
if (SCSIDevices[scsi_id][scsi_lun].Phase == SCSI_PHASE_STATUS) {
/* Command completed (either OK or error) - call the phase callback to complete the command. */
scsi_device_target_phase_callback(lun_type, id);
}
/* If the phase is DATA IN or DATA OUT, finish this here. */
}
void scsi_device_command_phase1(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t lun_type = SCSIDevices[scsi_id][scsi_lun].LunType;
uint8_t id = 0;
switch (lun_type)
{
case SCSI_DISK:
id = scsi_hard_disks[scsi_id][scsi_lun];
break;
case SCSI_CDROM:
id = scsi_cdrom_drives[scsi_id][scsi_lun];
break;
case SCSI_ZIP:
id = scsi_zip_drives[scsi_id][scsi_lun];
break;
default:
id = 0;
return;
}
/* Call the second phase. */
scsi_device_target_phase_callback(lun_type, id);
SCSIDevices[scsi_id][scsi_lun].Status = scsi_device_target_err_stat_to_scsi(lun_type, id);
/* Command second phase complete - call the callback to complete the command. */
scsi_device_target_phase_callback(lun_type, id);
}
int32_t *scsi_device_get_buf_len(uint8_t scsi_id, uint8_t scsi_lun)
{
return &SCSIDevices[scsi_id][scsi_lun].BufferLength;
}

81
src/scsi/scsi_device.h Normal file
View File

@@ -0,0 +1,81 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the generic SCSI device command handler.
*
* Version: @(#)scsi_device.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef SCSI_DEVICE_H
# define SCSI_DEVICE_H
typedef struct
{
int state;
int new_state;
int clear_req;
uint32_t bus_in, bus_out;
int dev_id;
int command_pos;
uint8_t command[20];
int data_pos;
int change_state_delay;
int new_req_delay;
} scsi_bus_t;
extern uint8_t *scsi_device_sense(uint8_t id, uint8_t lun);
extern void scsi_device_type_data(uint8_t id, uint8_t lun,
uint8_t *type, uint8_t *rmb);
extern void scsi_device_request_sense(uint8_t scsi_id, uint8_t scsi_lun,
uint8_t *buffer,
uint8_t alloc_length);
extern int scsi_device_read_capacity(uint8_t id, uint8_t lun,
uint8_t *cdb, uint8_t *buffer,
uint32_t *len);
extern int scsi_device_present(uint8_t id, uint8_t lun);
extern int scsi_device_valid(uint8_t id, uint8_t lun);
extern int scsi_device_cdb_length(uint8_t id, uint8_t lun);
extern void scsi_device_command(uint8_t id, uint8_t lun, int cdb_len,
uint8_t *cdb);
extern void scsi_device_command_phase0(uint8_t scsi_id, uint8_t scsi_lun,
int cdb_len, uint8_t *cdb);
extern void scsi_device_command_phase1(uint8_t scsi_id, uint8_t scsi_lun);
extern int32_t *scsi_device_get_buf_len(uint8_t scsi_id, uint8_t scsi_lun);
extern int scsi_bus_update(scsi_bus_t *bus, int bus_assert);
extern int scsi_bus_read(scsi_bus_t *bus);
extern int scsi_bus_match(scsi_bus_t *bus, int bus_assert);
#endif /*SCSI_DEVICE_H*/

1726
src/scsi/scsi_disk.c Normal file

File diff suppressed because it is too large Load Diff

100
src/scsi/scsi_disk.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Emulation of SCSI fixed and removable disks.
*
* Version: @(#)scsi_disk.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef SCSI_DISK_H
# define SCSI_DISK_H
typedef struct {
/* Stuff for SCSI hard disks. */
uint8_t cdb[16];
uint8_t current_cdb[16];
uint8_t max_cdb_len;
int requested_blocks;
int max_blocks_at_once;
uint16_t request_length;
int block_total;
int all_blocks_total;
uint32_t packet_len;
int packet_status;
uint8_t status;
uint8_t phase;
uint32_t pos;
int callback;
int total_read;
int unit_attention;
uint8_t sense[256];
uint8_t previous_command;
uint8_t error;
uint32_t sector_pos;
uint32_t sector_len;
uint32_t seek_pos;
int data_pos;
int old_len;
int request_pos;
uint8_t hd_cdb[16];
uint64_t current_page_code;
int current_page_len;
int current_page_pos;
int mode_select_phase;
int total_length;
int written_length;
int do_page_save;
int block_descriptor_len;
uint8_t *temp_buffer;
} scsi_hard_disk_t;
extern scsi_hard_disk_t shdc[HDD_NUM];
extern FILE *shdf[HDD_NUM];
extern void scsi_disk_insert(uint8_t id);
extern void scsi_loadhd(int scsi_id, int scsi_lun, int id);
extern void scsi_reloadhd(int id);
extern void scsi_unloadhd(int scsi_id, int scsi_lun, int id);
extern int scsi_hd_read_capacity(uint8_t id, uint8_t *cdb,
uint8_t *buffer, uint32_t *len);
#endif /*SCSI_DISK_H*/

1109
src/scsi/scsi_ncr5380.c Normal file

File diff suppressed because it is too large Load Diff

49
src/scsi/scsi_ncr5380.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Implementation of the NCR 5380 series of SCSI Host Adapters
* made by NCR. These controllers were designed for
* the ISA bus.
*
* Version: @(#)scsi_ncr5380.c 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef SCSI_NCR5380_H
# define SCSI_NCR5380_H
extern device_t scsi_lcs6821n_device;
extern device_t scsi_rt1000b_device;
extern device_t scsi_t130b_device;
extern device_t scsi_scsiat_device;
#endif /*SCSI_NCR5380_H*/

2125
src/scsi/scsi_ncr53c810.c Normal file

File diff suppressed because it is too large Load Diff

45
src/scsi/scsi_ncr53c810.h Normal file
View File

@@ -0,0 +1,45 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the NCR 53C810 SCSI Host Adapter driver.
*
* Version: @(#)scsi_ncr53c810.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* TheCollector1995, <mariogplayer@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef SCSI_NCR53C810_H
# define SCSI_NCR53C810_H
extern device_t ncr53c810_pci_device;
#endif /*SCSI_NCR53C810_H*/

2059
src/scsi/scsi_x54x.c Normal file

File diff suppressed because it is too large Load Diff

524
src/scsi/scsi_x54x.h Normal file
View File

@@ -0,0 +1,524 @@
/*
* VARCem Virtual Archaelogical Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the common AHA/BL code.
*
* Version: @(#)scsi_x54x.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* TheCollector1995, <mariogplayer@gmail.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef SCSI_X54X_H
# define SCSI_X54X_H
#define SCSI_DELAY_TM 1 /* was 50 */
#define ROM_SIZE 16384 /* one ROM is 16K */
#define NVR_SIZE 256 /* size of NVR */
/* EEPROM map and bit definitions. */
#define EE0_HOSTID 0x07 /* EE(0) [2:0] */
#define EE0_ALTFLOP 0x80 /* EE(0) [7] FDC at 370h */
#define EE1_IRQCH 0x07 /* EE(1) [3:0] */
#define EE1_DMACH 0x70 /* EE(1) [7:4] */
#define EE2_RMVOK 0x01 /* EE(2) [0] Support removable disks */
#define EE2_HABIOS 0x02 /* EE(2) [1] HA Bios Space Reserved */
#define EE2_INT19 0x04 /* EE(2) [2] HA Bios Controls INT19 */
#define EE2_DYNSCAN 0x08 /* EE(2) [3] Dynamically scan bus */
#define EE2_TWODRV 0x10 /* EE(2) [4] Allow more than 2 drives */
#define EE2_SEEKRET 0x20 /* EE(2) [5] Immediate return on seek */
#define EE2_EXT1G 0x80 /* EE(2) [7] Extended Translation >1GB */
#define EE3_SPEED 0x00 /* EE(3) [7:0] DMA Speed */
#define SPEED_33 0xFF
#define SPEED_50 0x00
#define SPEED_56 0x04
#define SPEED_67 0x01
#define SPEED_80 0x02
#define SPEED_10 0x03
#define EE4_FLOPTOK 0x80 /* EE(4) [7] Support Flopticals */
#define EE6_PARITY 0x01 /* EE(6) [0] parity check enable */
#define EE6_TERM 0x02 /* EE(6) [1] host term enable */
#define EE6_RSTBUS 0x04 /* EE(6) [2] reset SCSI bus on boot */
#define EEE_SYNC 0x01 /* EE(E) [0] Enable Sync Negotiation */
#define EEE_DISCON 0x02 /* EE(E) [1] Enable Disconnection */
#define EEE_FAST 0x04 /* EE(E) [2] Enable FAST SCSI */
#define EEE_START 0x08 /* EE(E) [3] Enable Start Unit */
/*
* Host Adapter I/O ports.
*
* READ Port x+0: STATUS
* WRITE Port x+0: CONTROL
*
* READ Port x+1: DATA
* WRITE Port x+1: COMMAND
*
* READ Port x+2: INTERRUPT STATUS
* WRITE Port x+2: (undefined?)
*
* R/W Port x+3: (undefined)
*/
/* WRITE CONTROL commands. */
#define CTRL_HRST 0x80 /* Hard reset */
#define CTRL_SRST 0x40 /* Soft reset */
#define CTRL_IRST 0x20 /* interrupt reset */
#define CTRL_SCRST 0x10 /* SCSI bus reset */
/* READ STATUS. */
#define STAT_STST 0x80 /* self-test in progress */
#define STAT_DFAIL 0x40 /* internal diagnostic failure */
#define STAT_INIT 0x20 /* mailbox initialization required */
#define STAT_IDLE 0x10 /* HBA is idle */
#define STAT_CDFULL 0x08 /* Command/Data output port is full */
#define STAT_DFULL 0x04 /* Data input port is full */
#define STAT_INVCMD 0x01 /* Invalid command */
/* READ/WRITE DATA. */
#define CMD_NOP 0x00 /* No operation */
#define CMD_MBINIT 0x01 /* mailbox initialization */
#define CMD_START_SCSI 0x02 /* Start SCSI command */
#define CMD_BIOSCMD 0x03 /* Execute ROM BIOS command */
#define CMD_INQUIRY 0x04 /* Adapter inquiry */
#define CMD_EMBOI 0x05 /* enable Mailbox Out Interrupt */
#define CMD_SELTIMEOUT 0x06 /* Set SEL timeout */
#define CMD_BUSON_TIME 0x07 /* set bus-On time */
#define CMD_BUSOFF_TIME 0x08 /* set bus-off time */
#define CMD_DMASPEED 0x09 /* set ISA DMA speed */
#define CMD_RETDEVS 0x0A /* return installed devices */
#define CMD_RETCONF 0x0B /* return configuration data */
#define CMD_TARGET 0x0C /* set HBA to target mode */
#define CMD_RETSETUP 0x0D /* return setup data */
#define CMD_WRITE_CH2 0x1A /* write channel 2 buffer */
#define CMD_READ_CH2 0x1B /* read channel 2 buffer */
#define CMD_ECHO 0x1F /* ECHO command data */
#define CMD_OPTIONS 0x21 /* set adapter options */
/* READ INTERRUPT STATUS. */
#define INTR_ANY 0x80 /* any interrupt */
#define INTR_SRCD 0x08 /* SCSI reset detected */
#define INTR_HACC 0x04 /* HA command complete */
#define INTR_MBOA 0x02 /* MBO empty */
#define INTR_MBIF 0x01 /* MBI full */
/* Structure for the INQUIRE_SETUP_INFORMATION reply. */
#pragma pack(push,1)
typedef struct {
uint8_t uOffset :4,
uTransferPeriod :3,
fSynchronous :1;
} ReplyInquireSetupInformationSynchronousValue;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
uint8_t fSynchronousInitiationEnabled :1,
fParityCheckingEnabled :1,
uReserved1 :6;
uint8_t uBusTransferRate;
uint8_t uPreemptTimeOnBus;
uint8_t uTimeOffBus;
uint8_t cMailbox;
addr24 MailboxAddress;
ReplyInquireSetupInformationSynchronousValue SynchronousValuesId0To7[8];
uint8_t uDisconnectPermittedId0To7;
uint8_t VendorSpecificData[28];
} ReplyInquireSetupInformation;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
uint8_t Count;
addr24 Address;
} MailboxInit_t;
#pragma pack(pop)
/*
* Mailbox Definitions.
*
* Mailbox Out (MBO) command values.
*/
#define MBO_FREE 0x00
#define MBO_START 0x01
#define MBO_ABORT 0x02
/* Mailbox In (MBI) status values. */
#define MBI_FREE 0x00
#define MBI_SUCCESS 0x01
#define MBI_ABORT 0x02
#define MBI_NOT_FOUND 0x03
#define MBI_ERROR 0x04
#pragma pack(push,1)
typedef struct {
uint8_t CmdStatus;
addr24 CCBPointer;
} Mailbox_t;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
uint32_t CCBPointer;
union {
struct {
uint8_t Reserved[3];
uint8_t ActionCode;
} out;
struct {
uint8_t HostStatus;
uint8_t TargetStatus;
uint8_t Reserved;
uint8_t CompletionCode;
} in;
} u;
} Mailbox32_t;
#pragma pack(pop)
/*
*
* CCB - SCSI Command Control Block
*
* The CCB is a superset of the CDB (Command Descriptor Block)
* and specifies detailed information about a SCSI command.
*
*/
/* Byte 0 Command Control Block Operation Code */
#define SCSI_INITIATOR_COMMAND 0x00
#define TARGET_MODE_COMMAND 0x01
#define SCATTER_GATHER_COMMAND 0x02
#define SCSI_INITIATOR_COMMAND_RES 0x03
#define SCATTER_GATHER_COMMAND_RES 0x04
#define BUS_RESET 0x81
/* Byte 1 Address and Direction Control */
#define CCB_TARGET_ID_SHIFT 0x06 /* CCB Op Code = 00, 02 */
#define CCB_INITIATOR_ID_SHIFT 0x06 /* CCB Op Code = 01 */
#define CCB_DATA_XFER_IN 0x01
#define CCB_DATA_XFER_OUT 0x02
#define CCB_LUN_MASK 0x07 /* Logical Unit Number */
/* Byte 2 SCSI_Command_Length - Length of SCSI CDB
Byte 3 Request Sense Allocation Length */
#define FOURTEEN_BYTES 0x00 /* Request Sense Buffer size */
#define NO_AUTO_REQUEST_SENSE 0x01 /* No Request Sense Buffer */
/* Bytes 4, 5 and 6 Data Length - Data transfer byte count */
/* Bytes 7, 8 and 9 Data Pointer - SGD List or Data Buffer */
/* Bytes 10, 11 and 12 Link Pointer - Next CCB in Linked List */
/* Byte 13 Command Link ID - TBD (I don't know yet) */
/* Byte 14 Host Status - Host Adapter status */
#define CCB_COMPLETE 0x00 /* CCB completed without error */
#define CCB_LINKED_COMPLETE 0x0A /* Linked command completed */
#define CCB_LINKED_COMPLETE_INT 0x0B /* Linked complete with intr */
#define CCB_SELECTION_TIMEOUT 0x11 /* Set SCSI selection timed out */
#define CCB_DATA_OVER_UNDER_RUN 0x12
#define CCB_UNEXPECTED_BUS_FREE 0x13 /* Trg dropped SCSI BSY */
#define CCB_PHASE_SEQUENCE_FAIL 0x14 /* Trg bus phase sequence fail */
#define CCB_BAD_MBO_COMMAND 0x15 /* MBO command not 0, 1 or 2 */
#define CCB_INVALID_OP_CODE 0x16 /* CCB invalid operation code */
#define CCB_BAD_LINKED_LUN 0x17 /* Linked CCB LUN diff from 1st */
#define CCB_INVALID_DIRECTION 0x18 /* Invalid target direction */
#define CCB_DUPLICATE_CCB 0x19 /* Duplicate CCB */
#define CCB_INVALID_CCB 0x1A /* Invalid CCB - bad parameter */
/* Byte 15 Target Status
See scsi.h files for these statuses.
Bytes 16 and 17 Reserved (must be 0)
Bytes 18 through 18+n-1, where n=size of CDB Command Descriptor Block */
#pragma pack(push,1)
typedef struct {
uint8_t Opcode;
uint8_t Reserved1 :3,
ControlByte :2,
TagQueued :1,
QueueTag :2;
uint8_t CdbLength;
uint8_t RequestSenseLength;
uint32_t DataLength;
uint32_t DataPointer;
uint8_t Reserved2[2];
uint8_t HostStatus;
uint8_t TargetStatus;
uint8_t Id;
uint8_t Lun :5,
LegacyTagEnable :1,
LegacyQueueTag :2;
uint8_t Cdb[12];
uint8_t Reserved3[6];
uint32_t SensePointer;
} CCB32;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
uint8_t Opcode;
uint8_t Lun :3,
ControlByte :2,
Id :3;
uint8_t CdbLength;
uint8_t RequestSenseLength;
addr24 DataLength;
addr24 DataPointer;
addr24 LinkPointer;
uint8_t LinkId;
uint8_t HostStatus;
uint8_t TargetStatus;
uint8_t Reserved[2];
uint8_t Cdb[12];
} CCB;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
uint8_t Opcode;
uint8_t Pad1 :3,
ControlByte :2,
Pad2 :3;
uint8_t CdbLength;
uint8_t RequestSenseLength;
uint8_t Pad3[9];
uint8_t CompletionCode; /* Only used by the 1542C/CF(/CP?) BIOS mailboxes */
uint8_t HostStatus;
uint8_t TargetStatus;
uint8_t Pad4[2];
uint8_t Cdb[12];
} CCBC;
#pragma pack(pop)
#pragma pack(push,1)
typedef union {
CCB32 new;
CCB old;
CCBC common;
} CCBU;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct {
CCBU CmdBlock;
uint8_t *RequestSenseBuffer;
uint32_t CCBPointer;
int Is24bit;
uint8_t TargetID,
LUN,
HostStatus,
TargetStatus,
MailboxCompletionCode;
} Req_t;
#pragma pack(pop)
typedef struct {
int8_t type; /* type of device */
char vendor[16]; /* name of device vendor */
char name[16]; /* name of device */
int8_t Irq;
uint8_t IrqEnabled;
int8_t DmaChannel;
int8_t HostID;
uint32_t Base;
uint8_t pos_regs[8]; /* MCA */
wchar_t *bios_path; /* path to BIOS image file */
uint32_t rom_addr; /* address of BIOS ROM */
uint16_t rom_ioaddr; /* offset in BIOS of I/O addr */
uint16_t rom_shram; /* index to shared RAM */
uint16_t rom_shramsz; /* size of shared RAM */
uint16_t rom_fwhigh; /* offset in BIOS of ver ID */
rom_t bios; /* BIOS memory descriptor */
rom_t uppersck; /* BIOS memory descriptor */
uint8_t *rom1; /* main BIOS image */
uint8_t *rom2; /* SCSI-Select image */
wchar_t *nvr_path; /* path to NVR image file */
uint8_t *nvr; /* EEPROM buffer */
int64_t ResetCB;
volatile uint8_t /* for multi-threading, keep */
Status, /* these volatile */
Interrupt;
Req_t Req;
uint8_t Geometry;
uint8_t Control;
uint8_t Command;
uint8_t CmdBuf[128];
uint8_t CmdParam;
uint32_t CmdParamLeft;
uint8_t DataBuf[65536];
uint16_t DataReply;
uint16_t DataReplyLeft;
volatile uint32_t
MailboxInit,
MailboxCount,
MailboxOutAddr,
MailboxOutPosCur,
MailboxInAddr,
MailboxInPosCur,
MailboxReq;
volatile int
Mbx24bit,
MailboxOutInterrupts;
volatile int
PendingInterrupt,
Lock;
uint8_t shadow_ram[128];
volatile uint8_t
MailboxIsBIOS,
ToRaise;
uint8_t shram_mode;
uint8_t sync;
uint8_t parity;
uint8_t dma_buffer[128];
volatile
uint32_t BIOSMailboxInit,
BIOSMailboxCount,
BIOSMailboxOutAddr,
BIOSMailboxOutPosCur,
BIOSMailboxReq,
Residue;
uint8_t BusOnTime,
BusOffTime,
ATBusSpeed;
char *fw_rev; /* The 4 bytes of the revision command information + 2 extra bytes for BusLogic */
uint16_t bus; /* Basically a copy of device flags */
uint8_t setup_info_len;
uint8_t max_id;
uint8_t pci_slot;
uint8_t bit32;
uint8_t lba_bios;
mem_mapping_t mmio_mapping;
uint8_t int_geom_writable;
uint8_t cdrom_boot;
/* Pointer to a structure of vendor-specific data that only the vendor-specific code can understand */
void *ven_data;
/* Pointer to a function that performs vendor-specific operation during the thread */
void (*ven_thread)(void *p);
/* Pointer to a function that executes the second parameter phase of the vendor-specific command */
void (*ven_cmd_phase1)(void *p);
/* Pointer to a function that gets the host adapter ID in case it has to be read from a non-standard location */
uint8_t (*ven_get_host_id)(void *p);
/* Pointer to a function that updates the IRQ in the vendor-specific space */
uint8_t (*ven_get_irq)(void *p);
/* Pointer to a function that updates the DMA channel in the vendor-specific space */
uint8_t (*ven_get_dma)(void *p);
/* Pointer to a function that returns whether command is fast */
uint8_t (*ven_cmd_is_fast)(void *p);
/* Pointer to a function that executes vendor-specific fast path commands */
uint8_t (*ven_fast_cmds)(void *p, uint8_t cmd);
/* Pointer to a function that gets the parameter length for vendor-specific commands */
uint8_t (*get_ven_param_len)(void *p);
/* Pointer to a function that executes vendor-specific commands and returns whether or not to suppress the IRQ */
uint8_t (*ven_cmds)(void *p);
/* Pointer to a function that fills in the vendor-specific setup data */
void (*get_ven_data)(void *p);
/* Pointer to a function that determines if the mode is aggressive */
uint8_t (*is_aggressive_mode)(void *p);
/* Pointer to a function that returns interrupt type (0 = edge, 1 = level) */
uint8_t (*interrupt_type)(void *p);
/* Pointer to a function that resets vendor-specific data */
void (*ven_reset)(void *p);
} x54x_t;
#pragma pack(push,1)
typedef struct
{
uint8_t command;
uint8_t lun:3,
reserved:2,
id:3;
union {
struct {
uint16_t cyl;
uint8_t head;
uint8_t sec;
} chs;
struct {
uint8_t lba0; /* MSB */
uint8_t lba1;
uint8_t lba2;
uint8_t lba3; /* LSB */
} lba;
} u;
uint8_t secount;
addr24 dma_address;
} BIOSCMD;
#pragma pack(pop)
#define lba32_blk(p) ((uint32_t)(p->u.lba.lba0<<24) | (p->u.lba.lba1<<16) | \
(p->u.lba.lba2<<8) | p->u.lba.lba3)
extern void x54x_reset_ctrl(x54x_t *dev, uint8_t Reset);
extern void x54x_busy(uint8_t set);
extern void x54x_thread_start(x54x_t *dev);
extern void x54x_set_wait_event(void);
extern uint8_t x54x_is_busy(void);
extern void x54x_buf_alloc(uint8_t id, uint8_t lun, int length);
extern void x54x_buf_free(uint8_t id, uint8_t lun);
extern uint8_t x54x_mbo_process(x54x_t *dev);
extern void x54x_wait_for_poll(void);
extern void x54x_io_set(x54x_t *dev, uint32_t base, uint8_t len);
extern void x54x_io_remove(x54x_t *dev, uint32_t base, uint8_t len);
extern void x54x_mem_init(x54x_t *dev, uint32_t addr);
extern void x54x_mem_enable(x54x_t *dev);
extern void x54x_mem_set_addr(x54x_t *dev, uint32_t base);
extern void x54x_mem_disable(x54x_t *dev);
extern void *x54x_init(device_t *info);
extern void x54x_close(void *priv);
extern void x54x_device_reset(void *priv);
#endif /*SCSI_X54X_H*/