a1246c73e0
http://cantaforda.com/cfcl/eryk/linux/pdisk/index.html this is the utility provided by mklinux and osX to manipulate the Apple Partition map.
1109 lines
24 KiB
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;
|
|
}
|