NetBSD/dist/pdisk/SCSI_media.c

1109 lines
24 KiB
C

/*
* SCSI_media.c -
*
* Written by Eryk Vershen
*/
/*
* Copyright 1997,1998 by Apple Computer, Inc.
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appears in all copies and
* that both the copyright notice and this permission notice appear in
* supporting documentation.
*
* APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
* NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// for printf() & sprintf()
#include <stdio.h>
// for malloc() & free()
#include <stdlib.h>
#include "DoSCSICommand.h"
#include "SCSI_media.h"
#include "util.h"
/*
* Defines
*/
#define DriverRefNumToSCSI(x) ((signed short) (~(x) - 32))
/*
* Types
*/
typedef struct SCSI_media *SCSI_MEDIA;
struct SCSI_media {
struct media m;
long bus;
long id;
};
struct bus_entry {
long bus;
long sort_value;
long max_id;
long master_id;
};
struct SCSI_manager {
long exists;
long kind;
long bus_count;
struct bus_entry *bus_list;
};
typedef struct SCSI_media_iterator *SCSI_MEDIA_ITERATOR;
struct SCSI_media_iterator {
struct media_iterator m;
long bus_index;
long bus;
long id;
};
struct linux_order_cache {
struct cache_item *first;
struct cache_item *last;
long next_disk;
long next_cdrom;
long loaded;
};
struct cache_item {
struct cache_item *next;
long bus;
long id;
long value;
long is_cdrom;
long unsure;
};
/*
* Global Constants
*/
enum {
kNoDevice = 0x00FF
};
enum {
kRequiredSCSIinquiryLength = 36
};
/*
* Global Variables
*/
static long scsi_inited = 0;
static struct SCSI_manager scsi_mgr;
static struct linux_order_cache linux_order;
/*
* Forward declarations
*/
int AsyncSCSIPresent(void);
void scsi_init(void);
SCSI_MEDIA new_scsi_media(void);
long read_scsi_media(MEDIA m, long long offset, unsigned long count, void *address);
long write_scsi_media(MEDIA m, long long offset, unsigned long count, void *address);
long close_scsi_media(MEDIA m);
long os_reload_scsi_media(MEDIA m);
long compute_id(long bus, long device);
int SCSI_ReadBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address);
int SCSI_WriteBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address);
int DoTestUnitReady(UInt8 targetID, int bus);
int DoReadCapacity(UInt32 id, UInt32 bus, UInt32 *blockCount, UInt32 *blockSize);
SCSI_MEDIA_ITERATOR new_scsi_iterator(void);
void reset_scsi_iterator(MEDIA_ITERATOR m);
char *step_scsi_iterator(MEDIA_ITERATOR m);
void delete_scsi_iterator(MEDIA_ITERATOR m);
void fill_bus_entry(struct bus_entry *entry, long bus);
/*long get_bus_sort_value(long bus);*/
int bus_entry_compare(const void* a, const void* b);
int DoInquiry(UInt32 id, UInt32 bus, UInt32 *devType);
void probe_all(void);
void probe_scsi_device(long bus, long id, int unsure);
long lookup_scsi_device(long bus, long id, int *is_cdrom, int *unsure);
long lookup_scsi_index(long index, int is_cdrom, long *bus, long *id);
void add_to_cache(long bus, long id, int is_cdrom, int unsure);
void init_linux_cache(void);
void clear_linux_cache(void);
void mark_linux_cache_loaded(void);
int linux_cache_loaded(void);
/*
* Routines
*/
int
AsyncSCSIPresent(void)
{
return (TrapAvailable(_SCSIAtomic));
}
void
scsi_init(void)
{
int i;
int old_scsi;
if (scsi_inited != 0) {
return;
}
scsi_inited = 1;
scsi_mgr.exists = 1;
scsi_mgr.kind = allocate_media_kind();
if (AsyncSCSIPresent()) {
AllocatePB();
scsi_mgr.bus_count = gSCSIHiBusID + 1;
old_scsi = 0;
} else {
scsi_mgr.bus_count = 1;
old_scsi = 1;
}
scsi_mgr.bus_list = (struct bus_entry *)
calloc(scsi_mgr.bus_count, sizeof(struct bus_entry));
if (scsi_mgr.bus_list == 0) {
scsi_mgr.bus_count = 0;
} else {
for (i = 0; i < scsi_mgr.bus_count; i++) {
if (old_scsi) {
scsi_mgr.bus_list[i].bus = 0xFF;
} else {
scsi_mgr.bus_list[i].bus = i;
}
fill_bus_entry(&scsi_mgr.bus_list[i], i);
}
qsort((void *)scsi_mgr.bus_list, /* address of array */
scsi_mgr.bus_count, /* number of elements */
sizeof(struct bus_entry), /* size of element */
bus_entry_compare); /* element comparison routine */
}
init_linux_cache();
}
void
fill_bus_entry(struct bus_entry *entry, long bus)
{
OSErr status;
SCSIBusInquiryPB pb;
long len;
long result;
long x, y;
if (!AsyncSCSIPresent()) {
entry->sort_value = 0;
entry->max_id = 7;
entry->master_id = 7;
return;
}
len = sizeof(SCSIBusInquiryPB);
clear_memory((Ptr) &pb, len);
pb.scsiPBLength = len;
pb.scsiFunctionCode = SCSIBusInquiry;
pb.scsiDevice.bus = bus;
status = SCSIAction((SCSI_PB *) &pb);
if (status != noErr) {
result = 6;
} else {
switch (pb.scsiHBAslotType) {
case scsiMotherboardBus: x = 0; break;
case scsiPDSBus: x = 1; break;
case scsiNuBus: x = 2; break;
case scsiPCIBus: x = 3; break;
case scsiFireWireBridgeBus: x = 4; break;
case scsiPCMCIABus: x = 5; break;
default: x = 7 + pb.scsiHBAslotType; break;
};
switch (pb.scsiFeatureFlags & scsiBusInternalExternalMask) {
case scsiBusInternal: y = 0; break;
case scsiBusInternalExternal: y = 1; break;
case scsiBusExternal: y = 2; break;
default:
case scsiBusInternalExternalUnknown: y = 3; break;
};
result = x * 4 + y;
}
entry->sort_value = result;
entry->max_id = pb.scsiMaxLUN;
entry->master_id = pb.scsiInitiatorID;
}
int
bus_entry_compare(const void* a, const void* b)
{
long result;
const struct bus_entry *x = (const struct bus_entry *) a;
const struct bus_entry *y = (const struct bus_entry *) b;
result = x->sort_value - y->sort_value;
if (result == 0) {
result = x->bus - y->bus;
}
return result;
}
SCSI_MEDIA
new_scsi_media(void)
{
return (SCSI_MEDIA) new_media(sizeof(struct SCSI_media));
}
MEDIA
open_old_scsi_as_media(long device)
{
return open_scsi_as_media(kOriginalSCSIBusAdaptor, device);
}
MEDIA
open_scsi_as_media(long bus, long device)
{
SCSI_MEDIA a;
UInt32 blockCount;
UInt32 blockSize;
if (scsi_inited == 0) {
scsi_init();
}
if (scsi_mgr.exists == 0) {
return 0;
}
a = 0;
if (DoTestUnitReady(device, bus) > 0) {
if (DoReadCapacity(device, bus, &blockCount, &blockSize) != 0) {
a = new_scsi_media();
if (a != 0) {
a->m.kind = scsi_mgr.kind;
a->m.grain = blockSize;
a->m.size_in_bytes = ((long long)blockCount) * blockSize;
a->m.do_read = read_scsi_media;
a->m.do_write = write_scsi_media;
a->m.do_close = close_scsi_media;
a->m.do_os_reload = os_reload_scsi_media;
a->bus = bus;
a->id = device;
}
}
}
return (MEDIA) a;
}
long
read_scsi_media(MEDIA m, long long offset, unsigned long count, void *address)
{
SCSI_MEDIA a;
long rtn_value;
long block;
long block_count;
long block_size;
unsigned char *buffer;
int i;
block = (long) offset;
//printf("scsi %d count %d\n", block, count);
a = (SCSI_MEDIA) m;
rtn_value = 0;
if (a == 0) {
/* no media */
} else if (a->m.kind != scsi_mgr.kind) {
/* wrong kind - XXX need to error here - this is an internal problem */
} else if (count <= 0 || count % a->m.grain != 0) {
/* can't handle size */
} else if (offset < 0 || offset % a->m.grain != 0) {
/* can't handle offset */
} else if (offset + count > a->m.size_in_bytes) {
/* check for offset (and offset+count) too large */
} else {
/* XXX do a read on the physical device */
block_size = a->m.grain;
block = offset / block_size;
block_count = count / block_size;
buffer = address;
rtn_value = 1;
for (i = 0; i < block_count; i++) {
if (SCSI_ReadBlock(a->id, a->bus, block_size, block, buffer) == 0) {
rtn_value = 0;
break;
}
buffer += block_size;
block += 1;
}
}
return rtn_value;
}
long
write_scsi_media(MEDIA m, long long offset, unsigned long count, void *address)
{
SCSI_MEDIA a;
long rtn_value;
long block;
long block_count;
long block_size;
unsigned char *buffer;
int i;
a = (SCSI_MEDIA) m;
rtn_value = 0;
if (a == 0) {
/* no media */
} else if (a->m.kind != scsi_mgr.kind) {
/* XXX need to error here - this is an internal problem */
} else if (count <= 0 || count % a->m.grain != 0) {
/* can't handle size */
} else if (offset < 0 || offset % a->m.grain != 0) {
/* can't handle offset */
} else if (offset + count > a->m.size_in_bytes) {
/* check for offset (and offset+count) too large */
} else {
/* XXX do a write on the physical device */
block_size = a->m.grain;
block = offset / block_size;
block_count = count / block_size;
buffer = address;
rtn_value = 1;
for (i = 0; i < block_count; i++) {
if (SCSI_WriteBlock(a->id, a->bus, block_size, block, buffer) == 0) {
rtn_value = 0;
break;
}
buffer += block_size;
block += 1;
}
}
return rtn_value;
}
long
close_scsi_media(MEDIA m)
{
SCSI_MEDIA a;
a = (SCSI_MEDIA) m;
if (a == 0) {
return 0;
} else if (a->m.kind != scsi_mgr.kind) {
/* XXX need to error here - this is an internal problem */
return 0;
}
/* XXX nothing to do - I think? */
return 1;
}
long
os_reload_scsi_media(MEDIA m)
{
printf("Reboot your system so the partition table will be reread.\n");
return 1;
}
#pragma mark -
int
DoTestUnitReady(UInt8 targetID, int bus)
{
OSErr status;
Str255 errorText;
char* msg;
static const SCSI_6_Byte_Command gTestUnitReadyCommand = {
kScsiCmdTestUnitReady, 0, 0, 0, 0, 0
};
SCSI_Sense_Data senseData;
DeviceIdent scsiDevice;
int rtn_value;
scsiDevice.diReserved = 0;
scsiDevice.bus = bus;
scsiDevice.targetID = targetID;
scsiDevice.LUN = 0;
status = DoSCSICommand(
scsiDevice,
"\pTest Unit Ready",
(SCSI_CommandPtr) &gTestUnitReadyCommand,
NULL,
0,
scsiDirectionNone,
NULL,
&senseData,
errorText
);
if (status == scsiNonZeroStatus) {
rtn_value = -1;
} else if (status != noErr) {
rtn_value = 0;
} else {
rtn_value = 1;
}
return rtn_value;
}
int
SCSI_ReadBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address)
{
OSErr status;
Str255 errorText;
char* msg;
static SCSI_10_Byte_Command gReadCommand = {
kScsiCmdRead10, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
SCSI_Sense_Data senseData;
DeviceIdent scsiDevice;
int rtn_value;
long count;
//printf("scsi read %d:%d block %d size %d\n", bus, id, block, block_size);
scsiDevice.diReserved = 0;
scsiDevice.bus = bus;
scsiDevice.targetID = id;
scsiDevice.LUN = 0;
gReadCommand.lbn4 = (block >> 24) & 0xFF;
gReadCommand.lbn3 = (block >> 16) & 0xFF;
gReadCommand.lbn2 = (block >> 8) & 0xFF;
gReadCommand.lbn1 = block & 0xFF;
count = 1;
gReadCommand.len2 = (count >> 8) & 0xFF;
gReadCommand.len1 = count & 0xFF;
status = DoSCSICommand(
scsiDevice,
"\pRead",
(SCSI_CommandPtr) &gReadCommand,
(Ptr) address,
count * block_size,
scsiDirectionIn,
NULL,
&senseData,
errorText
);
if (status == noErr) {
rtn_value = 1;
} else {
rtn_value = 0;
}
return rtn_value;
}
int
SCSI_WriteBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address)
{
OSErr status;
Str255 errorText;
char* msg;
static SCSI_10_Byte_Command gWriteCommand = {
kScsiCmdWrite10, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
SCSI_Sense_Data senseData;
DeviceIdent scsiDevice;
int rtn_value;
long count;
scsiDevice.diReserved = 0;
scsiDevice.bus = bus;
scsiDevice.targetID = id;
scsiDevice.LUN = 0;
gWriteCommand.lbn4 = (block >> 24) & 0xFF;
gWriteCommand.lbn3 = (block >> 16) & 0xFF;
gWriteCommand.lbn2 = (block >> 8) & 0xFF;
gWriteCommand.lbn1 = block & 0xFF;
count = 1;
gWriteCommand.len2 = (count >> 8) & 0xFF;
gWriteCommand.len1 = count & 0xFF;
status = DoSCSICommand(
scsiDevice,
"\pWrite",
(SCSI_CommandPtr) &gWriteCommand,
(Ptr) address,
count * block_size,
scsiDirectionOut,
NULL,
&senseData,
errorText
);
if (status == noErr) {
rtn_value = 1;
} else {
rtn_value = 0;
}
return rtn_value;
}
int
DoReadCapacity(UInt32 id, UInt32 bus, UInt32 *blockCount, UInt32 *blockSize)
{
OSErr status;
Str255 errorText;
static const SCSI_10_Byte_Command gCapacityCommand = {
kScsiCmdReadCapacity, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
SCSI_Sense_Data senseData;
DeviceIdent scsiDevice;
SCSI_Capacity_Data capacityData;
UInt32 temp;
int rtn_value;
scsiDevice.diReserved = 0;
scsiDevice.bus = bus;
scsiDevice.targetID = id;
scsiDevice.LUN = 0;
CLEAR(capacityData);
status = DoSCSICommand(
scsiDevice,
"\pRead Capacity",
(SCSI_CommandPtr) &gCapacityCommand,
(Ptr) &capacityData,
sizeof (SCSI_Capacity_Data),
scsiDirectionIn,
NULL,
&senseData,
errorText
);
if (status == noErr) {
temp = capacityData.lbn4;
temp = (temp << 8) | capacityData.lbn3;
temp = (temp << 8) | capacityData.lbn2;
temp = (temp << 8) | capacityData.lbn1;
*blockCount = temp;
temp = capacityData.len4;
temp = (temp << 8) | capacityData.len3;
temp = (temp << 8) | capacityData.len2;
temp = (temp << 8) | capacityData.len1;
*blockSize = temp;
rtn_value = 1;
} else {
rtn_value = 0;
}
return rtn_value;
}
int
DoInquiry(UInt32 id, UInt32 bus, UInt32 *devType)
{
OSErr status;
Str255 errorText;
static const SCSI_6_Byte_Command gInquiryCommand = {
kScsiCmdInquiry, 0, 0, 0, kRequiredSCSIinquiryLength, 0
};
SCSI_Sense_Data senseData;
DeviceIdent scsiDevice;
SCSI_Inquiry_Data inquiryData;
UInt32 temp;
int rtn_value;
scsiDevice.diReserved = 0;
scsiDevice.bus = bus;
scsiDevice.targetID = id;
scsiDevice.LUN = 0;
CLEAR(inquiryData);
status = DoSCSICommand(
scsiDevice,
"\pInquiry",
(SCSI_CommandPtr) &gInquiryCommand,
(Ptr) &inquiryData,
kRequiredSCSIinquiryLength,
scsiDirectionIn,
NULL,
&senseData,
errorText
);
if (status == noErr) {
*devType = inquiryData.devType & kScsiDevTypeMask;
rtn_value = 1;
} else {
rtn_value = 0;
}
return rtn_value;
}
MEDIA
SCSI_FindDevice(long dRefNum)
{
SCSIDriverPB pb;
OSErr status;
short targetID;
status = nsvErr;
if (AsyncSCSIPresent()) {
clear_memory((Ptr) &pb, sizeof pb);
pb.scsiPBLength = sizeof (SCSIDriverPB);
pb.scsiCompletion = NULL;
pb.scsiFlags = 0;
pb.scsiFunctionCode = SCSILookupRefNumXref;
pb.scsiDevice.bus = kNoDevice; /* was *((long *) &pb.scsiDevice) = 0xFFFFFFFFL; */
do {
status = SCSIAction((SCSI_PB *) &pb);
if (status != noErr) {
break;
} else if (pb.scsiDriver == dRefNum
&& pb.scsiDevice.bus != kNoDevice) {
return open_scsi_as_media(pb.scsiDevice.bus, pb.scsiDevice.targetID);
} else {
pb.scsiDevice = pb.scsiNextDevice;
}
}
while (pb.scsiDevice.bus != kNoDevice);
}
if (status == nsvErr) {
/*
* The asynchronous SCSI Manager is missing or the
* driver didn't register with the SCSI Manager.*/
targetID = DriverRefNumToSCSI(dRefNum);
if (targetID >= 0 && targetID <= 6) {
return open_old_scsi_as_media(targetID);
}
}
return 0;
}
#pragma mark -
SCSI_MEDIA_ITERATOR
new_scsi_iterator(void)
{
return (SCSI_MEDIA_ITERATOR) new_media_iterator(sizeof(struct SCSI_media_iterator));
}
MEDIA_ITERATOR
create_scsi_iterator(void)
{
SCSI_MEDIA_ITERATOR a;
if (scsi_inited == 0) {
scsi_init();
}
if (scsi_mgr.exists == 0) {
return 0;
}
a = new_scsi_iterator();
if (a != 0) {
a->m.kind = scsi_mgr.kind;
a->m.state = kInit;
a->m.do_reset = reset_scsi_iterator;
a->m.do_step = step_scsi_iterator;
a->m.do_delete = delete_scsi_iterator;
a->bus_index = 0;
a->bus = 0;
a->id = 0;
}
return (MEDIA_ITERATOR) a;
}
void
reset_scsi_iterator(MEDIA_ITERATOR m)
{
SCSI_MEDIA_ITERATOR a;
a = (SCSI_MEDIA_ITERATOR) m;
if (a == 0) {
/* no media */
} else if (a->m.kind != scsi_mgr.kind) {
/* wrong kind - XXX need to error here - this is an internal problem */
} else if (a->m.state != kInit) {
a->m.state = kReset;
}
}
char *
step_scsi_iterator(MEDIA_ITERATOR m)
{
SCSI_MEDIA_ITERATOR a;
char *result;
a = (SCSI_MEDIA_ITERATOR) m;
if (a == 0) {
/* no media */
} else if (a->m.kind != scsi_mgr.kind) {
/* wrong kind - XXX need to error here - this is an internal problem */
} else {
switch (a->m.state) {
case kInit:
/* find # of buses - done in AllocatePB() out of scsi_init() */
a->m.state = kReset;
/* fall through to reset */
case kReset:
a->bus_index = 0 /* first bus id */;
a->bus = scsi_mgr.bus_list[a->bus_index].bus;
a->id = 0 /* first device id */;
a->m.state = kIterating;
clear_linux_cache();
/* fall through to iterate */
case kIterating:
while (1) {
if (a->bus_index >= scsi_mgr.bus_count /* max bus id */) {
break;
}
if (a->id == scsi_mgr.bus_list[a->bus_index].master_id) {
/* next id */
a->id += 1;
}
if (a->id > scsi_mgr.bus_list[a->bus_index].max_id) {
a->bus_index += 1;
a->bus = scsi_mgr.bus_list[a->bus_index].bus;
a->id = 0 /* first device id */;
continue; /* try again */
}
/* generate result */
result = (char *) malloc(20);
if (result != NULL) {
if (a->bus == 0xFF) {
sprintf(result, "/dev/scsi%c", '0'+a->id);
probe_scsi_device(a->bus, a->id, 1);
} else {
// insure bus number in range
if (a->bus > 9) {
free(result);
result = NULL;
break;
}
sprintf(result, "/dev/scsi%c.%c", '0'+a->bus, '0'+a->id);
/* only probe out of iterate; so always added in order. */
probe_scsi_device(a->bus, a->id, 0);
}
}
a->id += 1; /* next id */
return result;
}
a->m.state = kEnd;
/* fall through to end */
case kEnd:
mark_linux_cache_loaded();
default:
break;
}
}
return 0 /* no entry */;
}
void
delete_scsi_iterator(MEDIA_ITERATOR m)
{
return;
}
#pragma mark -
MEDIA
open_linux_scsi_as_media(long index, int is_cdrom)
{
MEDIA m;
long bus;
long id;
if (lookup_scsi_index(index, is_cdrom, &bus, &id) > 0) {
m = open_scsi_as_media(bus, id);
} else {
m = 0;
}
return m;
}
char *
linux_old_scsi_name(long id)
{
linux_scsi_name(kOriginalSCSIBusAdaptor, id);
}
char *
linux_scsi_name(long bus, long id)
{
char *result = 0;
long value;
int is_cdrom;
int unsure;
char *suffix;
/* name is sda, sdb, sdc, ...
* in order by buses and ids, but only count responding devices ...
*/
if ((value = lookup_scsi_device(bus, id, &is_cdrom, &unsure)) >= 0) {
result = (char *) malloc(20);
if (result != NULL) {
if (unsure) {
suffix = " ?";
} else {
suffix = "";
}
if (is_cdrom) {
if (value > 9) {
// too many CD's, give up
free(result); result = NULL;
} else {
sprintf(result, "/dev/scd%c%s", '0' + value, suffix);
}
} else {
if (value < 26) {
sprintf(result, "/dev/sd%c%s", 'a' + value, suffix);
} else {
sprintf(result, "/dev/sd%c%c%s",
'a' + value / 26, 'a' + value % 26, suffix);
}
}
}
}
return result;
}
void
probe_all(void)
{
MEDIA_ITERATOR iter;
char *name;
iter = create_scsi_iterator();
if (iter == 0) {
return;
}
printf("finding devices ");
fflush(stdout);
while ((name = step_media_iterator(iter)) != 0) {
/* step does the probe for us */
printf(".");
fflush(stdout);
free(name);
}
delete_media_iterator(iter);
printf("\n");
fflush(stdout);
}
void
probe_scsi_device(long bus, long id, int unsure)
{
UInt32 devType;
if (DoInquiry(id, bus, &devType)) {
if (devType == kScsiDevTypeDirect
|| devType == kScsiDevTypeOptical) {
add_to_cache(bus, id, 0, unsure);
} else if (devType == kScsiDevTypeCDROM
|| devType == kScsiDevTypeWorm) {
add_to_cache(bus, id, 1, unsure);
}
}
}
long
lookup_scsi_device(long bus, long id, int *is_cdrom, int *unsure)
{
/* walk down list looking for bus and id ?
*
* only probe out of iterate (so always add in order)
* reset list if we reset the iterate
*/
struct cache_item *item;
struct cache_item *next;
long result = -1;
int count = 0;
if (scsi_inited == 0) {
scsi_init();
}
while (1) {
count++;
for (item = linux_order.first; item != NULL; item = item->next) {
if (item->bus == bus && item->id == id) {
result = item->value;
*is_cdrom = item->is_cdrom;
*unsure = item->unsure;
break;
}
}
if (count < 2 && result < 0) {
probe_all();
} else {
break;
}
};
return result;
}
/*
* This has the same structure as lookup_scsi_device() except we are
* matching on the value & type rather than the bus & id.
*/
long
lookup_scsi_index(long index, int is_cdrom, long *bus, long *id)
{
struct cache_item *item;
struct cache_item *next;
long result = 0;
int count = 0;
if (scsi_inited == 0) {
scsi_init();
}
while (1) {
count++;
for (item = linux_order.first; item != NULL; item = item->next) {
if (item->value == index && item->is_cdrom == is_cdrom
&& item->unsure == 0) {
result = 1;
*bus = item->bus;
*id = item->id;
break;
}
}
if (count < 2 && result == 0 && !linux_cache_loaded()) {
probe_all();
} else {
break;
}
};
return result;
}
void
add_to_cache(long bus, long id, int is_cdrom, int unsure)
{
struct cache_item *item;
item = malloc(sizeof(struct cache_item));
if (item == NULL) {
return;
} else {
item->bus = bus;
item->id = id;
item->is_cdrom = is_cdrom;
item->unsure = unsure;
if (is_cdrom) {
item->value = linux_order.next_cdrom;
linux_order.next_cdrom++;
} else {
item->value = linux_order.next_disk;
linux_order.next_disk++;
}
item->next = 0;
}
if (linux_order.first == NULL) {
linux_order.first = item;
linux_order.last = item;
} else {
linux_order.last->next = item;
linux_order.last = item;
}
}
void
init_linux_cache(void)
{
linux_order.first = NULL;
clear_linux_cache();
}
void
clear_linux_cache(void)
{
struct cache_item *item;
struct cache_item *next;
for (item = linux_order.first; item != NULL; item = next) {
next = item->next;
free(item);
}
/* back to starting value */
linux_order.first = NULL;
linux_order.last = NULL;
linux_order.next_disk = 0;
linux_order.next_cdrom = 0;
linux_order.loaded = 0;
}
void
mark_linux_cache_loaded(void)
{
linux_order.loaded = 1;
}
int
linux_cache_loaded(void)
{
return linux_order.loaded;
}