4 * Written by Eryk Vershen
8 * Copyright 1997,1998 by Apple Computer, Inc.
11 * Permission to use, copy, modify, and distribute this software and
12 * its documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appears in all copies and
14 * that both the copyright notice and this permission notice appear in
15 * supporting documentation.
17 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE.
21 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
22 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
24 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
25 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29 // for printf() & sprintf()
31 // for malloc() & free()
33 #include "DoSCSICommand.h"
34 #include "SCSI_media.h"
41 #define DriverRefNumToSCSI(x) ((signed short) (~(x) - 32))
47 typedef struct SCSI_media
*SCSI_MEDIA
;
66 struct bus_entry
*bus_list
;
69 typedef struct SCSI_media_iterator
*SCSI_MEDIA_ITERATOR
;
71 struct SCSI_media_iterator
{
72 struct media_iterator m
;
78 struct linux_order_cache
{
79 struct cache_item
*first
;
80 struct cache_item
*last
;
87 struct cache_item
*next
;
104 kRequiredSCSIinquiryLength
= 36
111 static long scsi_inited
= 0;
112 static struct SCSI_manager scsi_mgr
;
113 static struct linux_order_cache linux_order
;
117 * Forward declarations
119 int AsyncSCSIPresent(void);
120 void scsi_init(void);
121 SCSI_MEDIA
new_scsi_media(void);
122 long read_scsi_media(MEDIA m
, long long offset
, unsigned long count
, void *address
);
123 long write_scsi_media(MEDIA m
, long long offset
, unsigned long count
, void *address
);
124 long close_scsi_media(MEDIA m
);
125 long os_reload_scsi_media(MEDIA m
);
126 long compute_id(long bus
, long device
);
127 int SCSI_ReadBlock(UInt32 id
, UInt32 bus
, UInt32 block_size
, UInt32 block
, UInt8
*address
);
128 int SCSI_WriteBlock(UInt32 id
, UInt32 bus
, UInt32 block_size
, UInt32 block
, UInt8
*address
);
129 int DoTestUnitReady(UInt8 targetID
, int bus
);
130 int DoReadCapacity(UInt32 id
, UInt32 bus
, UInt32
*blockCount
, UInt32
*blockSize
);
131 SCSI_MEDIA_ITERATOR
new_scsi_iterator(void);
132 void reset_scsi_iterator(MEDIA_ITERATOR m
);
133 char *step_scsi_iterator(MEDIA_ITERATOR m
);
134 void delete_scsi_iterator(MEDIA_ITERATOR m
);
135 void fill_bus_entry(struct bus_entry
*entry
, long bus
);
136 /*long get_bus_sort_value(long bus);*/
137 int bus_entry_compare(const void* a
, const void* b
);
138 int DoInquiry(UInt32 id
, UInt32 bus
, UInt32
*devType
);
139 void probe_all(void);
140 void probe_scsi_device(long bus
, long id
, int unsure
);
141 long lookup_scsi_device(long bus
, long id
, int *is_cdrom
, int *unsure
);
142 long lookup_scsi_index(long index
, int is_cdrom
, long *bus
, long *id
);
143 void add_to_cache(long bus
, long id
, int is_cdrom
, int unsure
);
144 void init_linux_cache(void);
145 void clear_linux_cache(void);
146 void mark_linux_cache_loaded(void);
147 int linux_cache_loaded(void);
154 AsyncSCSIPresent(void)
156 return (TrapAvailable(_SCSIAtomic
));
166 if (scsi_inited
!= 0) {
172 scsi_mgr
.kind
= allocate_media_kind();
174 if (AsyncSCSIPresent()) {
177 scsi_mgr
.bus_count
= gSCSIHiBusID
+ 1;
180 scsi_mgr
.bus_count
= 1;
184 scsi_mgr
.bus_list
= (struct bus_entry
*)
185 calloc(scsi_mgr
.bus_count
, sizeof(struct bus_entry
));
187 if (scsi_mgr
.bus_list
== 0) {
188 scsi_mgr
.bus_count
= 0;
190 for (i
= 0; i
< scsi_mgr
.bus_count
; i
++) {
192 scsi_mgr
.bus_list
[i
].bus
= 0xFF;
194 scsi_mgr
.bus_list
[i
].bus
= i
;
196 fill_bus_entry(&scsi_mgr
.bus_list
[i
], i
);
198 qsort((void *)scsi_mgr
.bus_list
, /* address of array */
199 scsi_mgr
.bus_count
, /* number of elements */
200 sizeof(struct bus_entry
), /* size of element */
201 bus_entry_compare
); /* element comparison routine */
208 fill_bus_entry(struct bus_entry
*entry
, long bus
)
216 if (!AsyncSCSIPresent()) {
217 entry
->sort_value
= 0;
219 entry
->master_id
= 7;
222 len
= sizeof(SCSIBusInquiryPB
);
223 clear_memory((Ptr
) &pb
, len
);
224 pb
.scsiPBLength
= len
;
225 pb
.scsiFunctionCode
= SCSIBusInquiry
;
226 pb
.scsiDevice
.bus
= bus
;
227 status
= SCSIAction((SCSI_PB
*) &pb
);
228 if (status
!= noErr
) {
231 switch (pb
.scsiHBAslotType
) {
232 case scsiMotherboardBus
: x
= 0; break;
233 case scsiPDSBus
: x
= 1; break;
234 case scsiNuBus
: x
= 2; break;
235 case scsiPCIBus
: x
= 3; break;
236 case scsiFireWireBridgeBus
: x
= 4; break;
237 case scsiPCMCIABus
: x
= 5; break;
238 default: x
= 7 + pb
.scsiHBAslotType
; break;
241 switch (pb
.scsiFeatureFlags
& scsiBusInternalExternalMask
) {
242 case scsiBusInternal
: y
= 0; break;
243 case scsiBusInternalExternal
: y
= 1; break;
244 case scsiBusExternal
: y
= 2; break;
246 case scsiBusInternalExternalUnknown
: y
= 3; break;
250 entry
->sort_value
= result
;
251 entry
->max_id
= pb
.scsiMaxLUN
;
252 entry
->master_id
= pb
.scsiInitiatorID
;
257 bus_entry_compare(const void* a
, const void* b
)
261 const struct bus_entry
*x
= (const struct bus_entry
*) a
;
262 const struct bus_entry
*y
= (const struct bus_entry
*) b
;
264 result
= x
->sort_value
- y
->sort_value
;
266 result
= x
->bus
- y
->bus
;
275 return (SCSI_MEDIA
) new_media(sizeof(struct SCSI_media
));
280 open_old_scsi_as_media(long device
)
282 return open_scsi_as_media(kOriginalSCSIBusAdaptor
, device
);
287 open_scsi_as_media(long bus
, long device
)
293 if (scsi_inited
== 0) {
297 if (scsi_mgr
.exists
== 0) {
302 if (DoTestUnitReady(device
, bus
) > 0) {
303 if (DoReadCapacity(device
, bus
, &blockCount
, &blockSize
) != 0) {
304 a
= new_scsi_media();
306 a
->m
.kind
= scsi_mgr
.kind
;
307 a
->m
.grain
= blockSize
;
308 a
->m
.size_in_bytes
= ((long long)blockCount
) * blockSize
;
309 a
->m
.do_read
= read_scsi_media
;
310 a
->m
.do_write
= write_scsi_media
;
311 a
->m
.do_close
= close_scsi_media
;
312 a
->m
.do_os_reload
= os_reload_scsi_media
;
323 read_scsi_media(MEDIA m
, long long offset
, unsigned long count
, void *address
)
330 unsigned char *buffer
;
333 block
= (long) offset
;
334 //printf("scsi %d count %d\n", block, count);
339 } else if (a
->m
.kind
!= scsi_mgr
.kind
) {
340 /* wrong kind - XXX need to error here - this is an internal problem */
341 } else if (count
<= 0 || count
% a
->m
.grain
!= 0) {
342 /* can't handle size */
343 } else if (offset
< 0 || offset
% a
->m
.grain
!= 0) {
344 /* can't handle offset */
345 } else if (offset
+ count
> a
->m
.size_in_bytes
) {
346 /* check for offset (and offset+count) too large */
348 /* XXX do a read on the physical device */
349 block_size
= a
->m
.grain
;
350 block
= offset
/ block_size
;
351 block_count
= count
/ block_size
;
354 for (i
= 0; i
< block_count
; i
++) {
355 if (SCSI_ReadBlock(a
->id
, a
->bus
, block_size
, block
, buffer
) == 0) {
359 buffer
+= block_size
;
368 write_scsi_media(MEDIA m
, long long offset
, unsigned long count
, void *address
)
375 unsigned char *buffer
;
382 } else if (a
->m
.kind
!= scsi_mgr
.kind
) {
383 /* XXX need to error here - this is an internal problem */
384 } else if (count
<= 0 || count
% a
->m
.grain
!= 0) {
385 /* can't handle size */
386 } else if (offset
< 0 || offset
% a
->m
.grain
!= 0) {
387 /* can't handle offset */
388 } else if (offset
+ count
> a
->m
.size_in_bytes
) {
389 /* check for offset (and offset+count) too large */
391 /* XXX do a write on the physical device */
392 block_size
= a
->m
.grain
;
393 block
= offset
/ block_size
;
394 block_count
= count
/ block_size
;
397 for (i
= 0; i
< block_count
; i
++) {
398 if (SCSI_WriteBlock(a
->id
, a
->bus
, block_size
, block
, buffer
) == 0) {
402 buffer
+= block_size
;
411 close_scsi_media(MEDIA m
)
418 } else if (a
->m
.kind
!= scsi_mgr
.kind
) {
419 /* XXX need to error here - this is an internal problem */
422 /* XXX nothing to do - I think? */
428 os_reload_scsi_media(MEDIA m
)
430 printf("Reboot your system so the partition table will be reread.\n");
439 DoTestUnitReady(UInt8 targetID
, int bus
)
444 static const SCSI_6_Byte_Command gTestUnitReadyCommand
= {
445 kScsiCmdTestUnitReady
, 0, 0, 0, 0, 0
447 SCSI_Sense_Data senseData
;
448 DeviceIdent scsiDevice
;
451 scsiDevice
.diReserved
= 0;
452 scsiDevice
.bus
= bus
;
453 scsiDevice
.targetID
= targetID
;
456 status
= DoSCSICommand(
459 (SCSI_CommandPtr
) &gTestUnitReadyCommand
,
467 if (status
== scsiNonZeroStatus
) {
469 } else if (status
!= noErr
) {
479 SCSI_ReadBlock(UInt32 id
, UInt32 bus
, UInt32 block_size
, UInt32 block
, UInt8
*address
)
484 static SCSI_10_Byte_Command gReadCommand
= {
485 kScsiCmdRead10
, 0, 0, 0, 0, 0, 0, 0, 0, 0
487 SCSI_Sense_Data senseData
;
488 DeviceIdent scsiDevice
;
492 //printf("scsi read %d:%d block %d size %d\n", bus, id, block, block_size);
493 scsiDevice
.diReserved
= 0;
494 scsiDevice
.bus
= bus
;
495 scsiDevice
.targetID
= id
;
498 gReadCommand
.lbn4
= (block
>> 24) & 0xFF;
499 gReadCommand
.lbn3
= (block
>> 16) & 0xFF;
500 gReadCommand
.lbn2
= (block
>> 8) & 0xFF;
501 gReadCommand
.lbn1
= block
& 0xFF;
504 gReadCommand
.len2
= (count
>> 8) & 0xFF;
505 gReadCommand
.len1
= count
& 0xFF;
507 status
= DoSCSICommand(
510 (SCSI_CommandPtr
) &gReadCommand
,
518 if (status
== noErr
) {
528 SCSI_WriteBlock(UInt32 id
, UInt32 bus
, UInt32 block_size
, UInt32 block
, UInt8
*address
)
533 static SCSI_10_Byte_Command gWriteCommand
= {
534 kScsiCmdWrite10
, 0, 0, 0, 0, 0, 0, 0, 0, 0
536 SCSI_Sense_Data senseData
;
537 DeviceIdent scsiDevice
;
541 scsiDevice
.diReserved
= 0;
542 scsiDevice
.bus
= bus
;
543 scsiDevice
.targetID
= id
;
546 gWriteCommand
.lbn4
= (block
>> 24) & 0xFF;
547 gWriteCommand
.lbn3
= (block
>> 16) & 0xFF;
548 gWriteCommand
.lbn2
= (block
>> 8) & 0xFF;
549 gWriteCommand
.lbn1
= block
& 0xFF;
552 gWriteCommand
.len2
= (count
>> 8) & 0xFF;
553 gWriteCommand
.len1
= count
& 0xFF;
555 status
= DoSCSICommand(
558 (SCSI_CommandPtr
) &gWriteCommand
,
566 if (status
== noErr
) {
576 DoReadCapacity(UInt32 id
, UInt32 bus
, UInt32
*blockCount
, UInt32
*blockSize
)
580 static const SCSI_10_Byte_Command gCapacityCommand
= {
581 kScsiCmdReadCapacity
, 0, 0, 0, 0, 0, 0, 0, 0, 0
583 SCSI_Sense_Data senseData
;
584 DeviceIdent scsiDevice
;
585 SCSI_Capacity_Data capacityData
;
589 scsiDevice
.diReserved
= 0;
590 scsiDevice
.bus
= bus
;
591 scsiDevice
.targetID
= id
;
596 status
= DoSCSICommand(
599 (SCSI_CommandPtr
) &gCapacityCommand
,
601 sizeof (SCSI_Capacity_Data
),
608 if (status
== noErr
) {
609 temp
= capacityData
.lbn4
;
610 temp
= (temp
<< 8) | capacityData
.lbn3
;
611 temp
= (temp
<< 8) | capacityData
.lbn2
;
612 temp
= (temp
<< 8) | capacityData
.lbn1
;
615 temp
= capacityData
.len4
;
616 temp
= (temp
<< 8) | capacityData
.len3
;
617 temp
= (temp
<< 8) | capacityData
.len2
;
618 temp
= (temp
<< 8) | capacityData
.len1
;
630 DoInquiry(UInt32 id
, UInt32 bus
, UInt32
*devType
)
634 static const SCSI_6_Byte_Command gInquiryCommand
= {
635 kScsiCmdInquiry
, 0, 0, 0, kRequiredSCSIinquiryLength
, 0
637 SCSI_Sense_Data senseData
;
638 DeviceIdent scsiDevice
;
639 SCSI_Inquiry_Data inquiryData
;
643 scsiDevice
.diReserved
= 0;
644 scsiDevice
.bus
= bus
;
645 scsiDevice
.targetID
= id
;
650 status
= DoSCSICommand(
653 (SCSI_CommandPtr
) &gInquiryCommand
,
655 kRequiredSCSIinquiryLength
,
662 if (status
== noErr
) {
663 *devType
= inquiryData
.devType
& kScsiDevTypeMask
;
673 SCSI_FindDevice(long dRefNum
)
680 if (AsyncSCSIPresent()) {
681 clear_memory((Ptr
) &pb
, sizeof pb
);
683 pb
.scsiPBLength
= sizeof (SCSIDriverPB
);
684 pb
.scsiCompletion
= NULL
;
686 pb
.scsiFunctionCode
= SCSILookupRefNumXref
;
687 pb
.scsiDevice
.bus
= kNoDevice
; /* was *((long *) &pb.scsiDevice) = 0xFFFFFFFFL; */
690 status
= SCSIAction((SCSI_PB
*) &pb
);
692 if (status
!= noErr
) {
694 } else if (pb
.scsiDriver
== dRefNum
695 && pb
.scsiDevice
.bus
!= kNoDevice
) {
696 return open_scsi_as_media(pb
.scsiDevice
.bus
, pb
.scsiDevice
.targetID
);
699 pb
.scsiDevice
= pb
.scsiNextDevice
;
702 while (pb
.scsiDevice
.bus
!= kNoDevice
);
704 if (status
== nsvErr
) {
706 * The asynchronous SCSI Manager is missing or the
707 * driver didn't register with the SCSI Manager.*/
708 targetID
= DriverRefNumToSCSI(dRefNum
);
709 if (targetID
>= 0 && targetID
<= 6) {
710 return open_old_scsi_as_media(targetID
);
721 new_scsi_iterator(void)
723 return (SCSI_MEDIA_ITERATOR
) new_media_iterator(sizeof(struct SCSI_media_iterator
));
728 create_scsi_iterator(void)
730 SCSI_MEDIA_ITERATOR a
;
732 if (scsi_inited
== 0) {
736 if (scsi_mgr
.exists
== 0) {
740 a
= new_scsi_iterator();
742 a
->m
.kind
= scsi_mgr
.kind
;
744 a
->m
.do_reset
= reset_scsi_iterator
;
745 a
->m
.do_step
= step_scsi_iterator
;
746 a
->m
.do_delete
= delete_scsi_iterator
;
752 return (MEDIA_ITERATOR
) a
;
757 reset_scsi_iterator(MEDIA_ITERATOR m
)
759 SCSI_MEDIA_ITERATOR a
;
761 a
= (SCSI_MEDIA_ITERATOR
) m
;
764 } else if (a
->m
.kind
!= scsi_mgr
.kind
) {
765 /* wrong kind - XXX need to error here - this is an internal problem */
766 } else if (a
->m
.state
!= kInit
) {
773 step_scsi_iterator(MEDIA_ITERATOR m
)
775 SCSI_MEDIA_ITERATOR a
;
778 a
= (SCSI_MEDIA_ITERATOR
) m
;
781 } else if (a
->m
.kind
!= scsi_mgr
.kind
) {
782 /* wrong kind - XXX need to error here - this is an internal problem */
784 switch (a
->m
.state
) {
786 /* find # of buses - done in AllocatePB() out of scsi_init() */
788 /* fall through to reset */
790 a
->bus_index
= 0 /* first bus id */;
791 a
->bus
= scsi_mgr
.bus_list
[a
->bus_index
].bus
;
792 a
->id
= 0 /* first device id */;
793 a
->m
.state
= kIterating
;
795 /* fall through to iterate */
798 if (a
->bus_index
>= scsi_mgr
.bus_count
/* max bus id */) {
801 if (a
->id
== scsi_mgr
.bus_list
[a
->bus_index
].master_id
) {
805 if (a
->id
> scsi_mgr
.bus_list
[a
->bus_index
].max_id
) {
807 a
->bus
= scsi_mgr
.bus_list
[a
->bus_index
].bus
;
808 a
->id
= 0 /* first device id */;
809 continue; /* try again */
811 /* generate result */
812 result
= (char *) malloc(20);
813 if (result
!= NULL
) {
814 if (a
->bus
== 0xFF) {
815 snprintf(result
, 20, "/dev/scsi%c", '0'+a
->id
);
816 probe_scsi_device(a
->bus
, a
->id
, 1);
818 // insure bus number in range
824 snprintf(result
, 20, "/dev/scsi%c.%c",
825 '0'+a
->bus
, '0'+a
->id
);
826 /* only probe out of iterate; so always added in order. */
827 probe_scsi_device(a
->bus
, a
->id
, 0);
831 a
->id
+= 1; /* next id */
835 /* fall through to end */
837 mark_linux_cache_loaded();
842 return 0 /* no entry */;
847 delete_scsi_iterator(MEDIA_ITERATOR m
)
857 open_linux_scsi_as_media(long index
, int is_cdrom
)
863 if (lookup_scsi_index(index
, is_cdrom
, &bus
, &id
) > 0) {
864 m
= open_scsi_as_media(bus
, id
);
874 linux_old_scsi_name(long id
)
876 linux_scsi_name(kOriginalSCSIBusAdaptor
, id
);
881 linux_scsi_name(long bus
, long id
)
889 /* name is sda, sdb, sdc, ...
890 * in order by buses and ids, but only count responding devices ...
892 if ((value
= lookup_scsi_device(bus
, id
, &is_cdrom
, &unsure
)) >= 0) {
893 result
= (char *) malloc(20);
894 if (result
!= NULL
) {
902 // too many CD's, give up
903 free(result
); result
= NULL
;
905 snprintf(result
, 20, "/dev/scd%c%s", '0' + value
, suffix
);
909 snprintf(result
, 20, "/dev/sd%c%s", 'a' + value
, suffix
);
911 snprintf(result
, 20, "/dev/sd%c%c%s",
912 'a' + value
/ 26, 'a' + value
% 26, suffix
);
927 iter
= create_scsi_iterator();
932 printf("finding devices ");
934 while ((name
= step_media_iterator(iter
)) != 0) {
935 /* step does the probe for us */
940 delete_media_iterator(iter
);
947 probe_scsi_device(long bus
, long id
, int unsure
)
951 if (DoInquiry(id
, bus
, &devType
)) {
952 if (devType
== kScsiDevTypeDirect
953 || devType
== kScsiDevTypeOptical
) {
954 add_to_cache(bus
, id
, 0, unsure
);
955 } else if (devType
== kScsiDevTypeCDROM
956 || devType
== kScsiDevTypeWorm
) {
957 add_to_cache(bus
, id
, 1, unsure
);
964 lookup_scsi_device(long bus
, long id
, int *is_cdrom
, int *unsure
)
966 /* walk down list looking for bus and id ?
968 * only probe out of iterate (so always add in order)
969 * reset list if we reset the iterate
971 struct cache_item
*item
;
972 struct cache_item
*next
;
976 if (scsi_inited
== 0) {
982 for (item
= linux_order
.first
; item
!= NULL
; item
= item
->next
) {
983 if (item
->bus
== bus
&& item
->id
== id
) {
984 result
= item
->value
;
985 *is_cdrom
= item
->is_cdrom
;
986 *unsure
= item
->unsure
;
990 if (count
< 2 && result
< 0) {
1002 * This has the same structure as lookup_scsi_device() except we are
1003 * matching on the value & type rather than the bus & id.
1006 lookup_scsi_index(long index
, int is_cdrom
, long *bus
, long *id
)
1008 struct cache_item
*item
;
1009 struct cache_item
*next
;
1013 if (scsi_inited
== 0) {
1019 for (item
= linux_order
.first
; item
!= NULL
; item
= item
->next
) {
1020 if (item
->value
== index
&& item
->is_cdrom
== is_cdrom
1021 && item
->unsure
== 0) {
1028 if (count
< 2 && result
== 0 && !linux_cache_loaded()) {
1040 add_to_cache(long bus
, long id
, int is_cdrom
, int unsure
)
1042 struct cache_item
*item
;
1044 item
= malloc(sizeof(struct cache_item
));
1050 item
->is_cdrom
= is_cdrom
;
1051 item
->unsure
= unsure
;
1053 item
->value
= linux_order
.next_cdrom
;
1054 linux_order
.next_cdrom
++;
1056 item
->value
= linux_order
.next_disk
;
1057 linux_order
.next_disk
++;
1061 if (linux_order
.first
== NULL
) {
1062 linux_order
.first
= item
;
1063 linux_order
.last
= item
;
1065 linux_order
.last
->next
= item
;
1066 linux_order
.last
= item
;
1072 init_linux_cache(void)
1074 linux_order
.first
= NULL
;
1075 clear_linux_cache();
1080 clear_linux_cache(void)
1082 struct cache_item
*item
;
1083 struct cache_item
*next
;
1085 for (item
= linux_order
.first
; item
!= NULL
; item
= next
) {
1089 /* back to starting value */
1090 linux_order
.first
= NULL
;
1091 linux_order
.last
= NULL
;
1092 linux_order
.next_disk
= 0;
1093 linux_order
.next_cdrom
= 0;
1094 linux_order
.loaded
= 0;
1099 mark_linux_cache_loaded(void)
1101 linux_order
.loaded
= 1;
1106 linux_cache_loaded(void)
1108 return linux_order
.loaded
;