1/*
2 * SCSI_media.c -
3 *
4 * Written by Eryk Vershen
5 */
6
7/*
8 * Copyright 1997,1998 by Apple Computer, Inc.
9 *              All Rights Reserved
10 *
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.
16 *
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.
20 *
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.
26 */
27
28
29// for printf() & sprintf()
30#include <stdio.h>
31// for malloc() & free()
32#include <stdlib.h>
33#include "DoSCSICommand.h"
34#include "SCSI_media.h"
35#include "util.h"
36
37
38/*
39 * Defines
40 */
41#define DriverRefNumToSCSI(x)  ((signed short) (~(x) - 32))
42
43
44/*
45 * Types
46 */
47typedef struct SCSI_media *SCSI_MEDIA;
48
49struct SCSI_media {
50    struct media    m;
51    long            bus;
52    long            id;
53};
54
55struct bus_entry {
56    long    bus;
57    long    sort_value;
58    long    max_id;
59    long    master_id;
60};
61
62struct SCSI_manager {
63    long        exists;
64    long        kind;
65    long        bus_count;
66    struct bus_entry *bus_list;
67};
68
69typedef struct SCSI_media_iterator *SCSI_MEDIA_ITERATOR;
70
71struct SCSI_media_iterator {
72    struct media_iterator   m;
73    long                    bus_index;
74    long                    bus;
75    long                    id;
76};
77
78struct linux_order_cache {
79    struct cache_item *first;
80    struct cache_item *last;
81    long next_disk;
82    long next_cdrom;
83    long loaded;
84};
85
86struct cache_item {
87    struct cache_item *next;
88    long bus;
89    long id;
90    long value;
91    long is_cdrom;
92    long unsure;
93};
94
95
96/*
97 * Global Constants
98 */
99enum {
100    kNoDevice = 0x00FF
101};
102
103enum {
104    kRequiredSCSIinquiryLength = 36
105};
106
107
108/*
109 * Global Variables
110 */
111static long scsi_inited = 0;
112static struct SCSI_manager scsi_mgr;
113static struct linux_order_cache linux_order;
114
115
116/*
117 * Forward declarations
118 */
119int AsyncSCSIPresent(void);
120void scsi_init(void);
121SCSI_MEDIA new_scsi_media(void);
122long read_scsi_media(MEDIA m, long long offset, unsigned long count, void *address);
123long write_scsi_media(MEDIA m, long long offset, unsigned long count, void *address);
124long close_scsi_media(MEDIA m);
125long os_reload_scsi_media(MEDIA m);
126long compute_id(long bus, long device);
127int SCSI_ReadBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address);
128int SCSI_WriteBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address);
129int DoTestUnitReady(UInt8 targetID, int bus);
130int DoReadCapacity(UInt32 id, UInt32 bus, UInt32 *blockCount, UInt32 *blockSize);
131SCSI_MEDIA_ITERATOR new_scsi_iterator(void);
132void reset_scsi_iterator(MEDIA_ITERATOR m);
133char *step_scsi_iterator(MEDIA_ITERATOR m);
134void delete_scsi_iterator(MEDIA_ITERATOR m);
135void fill_bus_entry(struct bus_entry *entry, long bus);
136/*long get_bus_sort_value(long bus);*/
137int bus_entry_compare(const void* a, const void* b);
138int DoInquiry(UInt32 id, UInt32 bus, UInt32 *devType);
139void probe_all(void);
140void probe_scsi_device(long bus, long id, int unsure);
141long lookup_scsi_device(long bus, long id, int *is_cdrom, int *unsure);
142long lookup_scsi_index(long index, int is_cdrom, long *bus, long *id);
143void add_to_cache(long bus, long id, int is_cdrom, int unsure);
144void init_linux_cache(void);
145void clear_linux_cache(void);
146void mark_linux_cache_loaded(void);
147int linux_cache_loaded(void);
148
149
150/*
151 * Routines
152 */
153int
154AsyncSCSIPresent(void)
155{
156    return (TrapAvailable(_SCSIAtomic));
157}
158
159
160void
161scsi_init(void)
162{
163    int i;
164    int old_scsi;
165
166    if (scsi_inited != 0) {
167	return;
168    }
169    scsi_inited = 1;
170
171    scsi_mgr.exists = 1;
172    scsi_mgr.kind = allocate_media_kind();
173
174    if (AsyncSCSIPresent()) {
175	AllocatePB();
176
177	scsi_mgr.bus_count = gSCSIHiBusID + 1;
178	old_scsi = 0;
179    } else {
180	scsi_mgr.bus_count = 1;
181	old_scsi = 1;
182    }
183
184    scsi_mgr.bus_list = (struct bus_entry *)
185	    calloc(scsi_mgr.bus_count, sizeof(struct bus_entry));
186
187    if (scsi_mgr.bus_list == 0) {
188	scsi_mgr.bus_count = 0;
189    } else {
190	for (i = 0; i < scsi_mgr.bus_count; i++) {
191	    if (old_scsi) {
192		scsi_mgr.bus_list[i].bus = 0xFF;
193	    } else {
194		scsi_mgr.bus_list[i].bus = i;
195	    }
196	    fill_bus_entry(&scsi_mgr.bus_list[i], i);
197	}
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 */
202    }
203
204    init_linux_cache();
205}
206
207void
208fill_bus_entry(struct bus_entry *entry, long bus)
209{
210    OSErr           status;
211    SCSIBusInquiryPB    pb;
212    long len;
213    long result;
214    long x, y;
215
216    if (!AsyncSCSIPresent()) {
217    	entry->sort_value = 0;
218	entry->max_id = 7;
219	entry->master_id = 7;
220	return;
221    }
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) {
229	result = 6;
230    } else {
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;
239	};
240
241	switch (pb.scsiFeatureFlags & scsiBusInternalExternalMask) {
242	case scsiBusInternal:                   y = 0; break;
243	case scsiBusInternalExternal:           y = 1; break;
244	case scsiBusExternal:                   y = 2; break;
245	default:
246	case scsiBusInternalExternalUnknown:    y = 3; break;
247	};
248	result = x * 4 + y;
249    }
250    entry->sort_value = result;
251    entry->max_id = pb.scsiMaxLUN;
252    entry->master_id = pb.scsiInitiatorID;
253}
254
255
256int
257bus_entry_compare(const void* a, const void* b)
258{
259    long result;
260
261    const struct bus_entry *x = (const struct bus_entry *) a;
262    const struct bus_entry *y = (const struct bus_entry *) b;
263
264    result = x->sort_value - y->sort_value;
265    if (result == 0) {
266	result = x->bus - y->bus;
267    }
268    return result;
269}
270
271
272SCSI_MEDIA
273new_scsi_media(void)
274{
275    return (SCSI_MEDIA) new_media(sizeof(struct SCSI_media));
276}
277
278
279MEDIA
280open_old_scsi_as_media(long device)
281{
282    return open_scsi_as_media(kOriginalSCSIBusAdaptor, device);
283}
284
285
286MEDIA
287open_scsi_as_media(long bus, long device)
288{
289    SCSI_MEDIA  a;
290    UInt32 blockCount;
291    UInt32 blockSize;
292
293    if (scsi_inited == 0) {
294	scsi_init();
295    }
296
297    if (scsi_mgr.exists == 0) {
298	return 0;
299    }
300
301    a = 0;
302    if (DoTestUnitReady(device, bus) > 0) {
303	if (DoReadCapacity(device, bus, &blockCount, &blockSize) != 0) {
304	    a = new_scsi_media();
305	    if (a != 0) {
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;
313		a->bus = bus;
314		a->id = device;
315	    }
316	}
317    }
318    return (MEDIA) a;
319}
320
321
322long
323read_scsi_media(MEDIA m, long long offset, unsigned long count, void *address)
324{
325    SCSI_MEDIA a;
326    long rtn_value;
327    long block;
328    long block_count;
329    long block_size;
330    unsigned char *buffer;
331    int i;
332
333    block = (long) offset;
334//printf("scsi %d count %d\n", block, count);
335    a = (SCSI_MEDIA) m;
336    rtn_value = 0;
337    if (a == 0) {
338	/* no media */
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 */
347    } else {
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;
352	buffer = address;
353	rtn_value = 1;
354	for (i = 0; i < block_count; i++) {
355	    if (SCSI_ReadBlock(a->id, a->bus, block_size, block, buffer) == 0) {
356		rtn_value = 0;
357		break;
358	    }
359	    buffer += block_size;
360	    block += 1;
361	}
362    }
363    return rtn_value;
364}
365
366
367long
368write_scsi_media(MEDIA m, long long offset, unsigned long count, void *address)
369{
370    SCSI_MEDIA a;
371    long rtn_value;
372    long block;
373    long block_count;
374    long block_size;
375    unsigned char *buffer;
376    int i;
377
378    a = (SCSI_MEDIA) m;
379    rtn_value = 0;
380    if (a == 0) {
381	/* no media */
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 */
390    } else {
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;
395	buffer = address;
396	rtn_value = 1;
397	for (i = 0; i < block_count; i++) {
398	    if (SCSI_WriteBlock(a->id, a->bus, block_size, block, buffer) == 0) {
399		rtn_value = 0;
400		break;
401	    }
402	    buffer += block_size;
403	    block += 1;
404	}
405    }
406    return rtn_value;
407}
408
409
410long
411close_scsi_media(MEDIA m)
412{
413    SCSI_MEDIA a;
414
415    a = (SCSI_MEDIA) m;
416    if (a == 0) {
417	return 0;
418    } else if (a->m.kind != scsi_mgr.kind) {
419	/* XXX need to error here - this is an internal problem */
420	return 0;
421    }
422    /* XXX nothing to do - I think? */
423    return 1;
424}
425
426
427long
428os_reload_scsi_media(MEDIA m)
429{
430    printf("Reboot your system so the partition table will be reread.\n");
431    return 1;
432}
433
434
435#pragma mark -
436
437
438int
439DoTestUnitReady(UInt8 targetID, int bus)
440{
441    OSErr                   status;
442    Str255                  errorText;
443    char*       msg;
444    static const SCSI_6_Byte_Command gTestUnitReadyCommand = {
445	kScsiCmdTestUnitReady, 0, 0, 0, 0, 0
446    };
447    SCSI_Sense_Data         senseData;
448    DeviceIdent scsiDevice;
449    int rtn_value;
450
451    scsiDevice.diReserved = 0;
452    scsiDevice.bus = bus;
453    scsiDevice.targetID = targetID;
454    scsiDevice.LUN = 0;
455
456    status = DoSCSICommand(
457		scsiDevice,
458		"\pTest Unit Ready",
459		(SCSI_CommandPtr) &gTestUnitReadyCommand,
460		NULL,
461		0,
462		scsiDirectionNone,
463		NULL,
464		&senseData,
465		errorText
466		);
467    if (status == scsiNonZeroStatus) {
468	rtn_value = -1;
469    } else if (status != noErr) {
470	rtn_value = 0;
471    } else {
472	rtn_value = 1;
473    }
474    return rtn_value;
475}
476
477
478int
479SCSI_ReadBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address)
480{
481    OSErr                   status;
482    Str255                  errorText;
483    char*       msg;
484    static SCSI_10_Byte_Command gReadCommand = {
485	kScsiCmdRead10, 0, 0, 0, 0, 0, 0, 0, 0, 0
486    };
487    SCSI_Sense_Data         senseData;
488    DeviceIdent scsiDevice;
489    int rtn_value;
490    long count;
491
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;
496    scsiDevice.LUN = 0;
497
498    gReadCommand.lbn4 = (block >> 24) & 0xFF;
499    gReadCommand.lbn3 = (block >> 16) & 0xFF;
500    gReadCommand.lbn2 = (block >> 8) & 0xFF;
501    gReadCommand.lbn1 = block & 0xFF;
502
503    count = 1;
504    gReadCommand.len2 = (count >> 8) & 0xFF;
505    gReadCommand.len1 = count & 0xFF;
506
507    status = DoSCSICommand(
508		scsiDevice,
509		"\pRead",
510		(SCSI_CommandPtr) &gReadCommand,
511		(Ptr) address,
512		count * block_size,
513		scsiDirectionIn,
514		NULL,
515		&senseData,
516		errorText
517	);
518    if (status == noErr) {
519	rtn_value = 1;
520    } else {
521	rtn_value = 0;
522    }
523    return rtn_value;
524}
525
526
527int
528SCSI_WriteBlock(UInt32 id, UInt32 bus, UInt32 block_size, UInt32 block, UInt8 *address)
529{
530    OSErr                   status;
531    Str255                  errorText;
532    char*       msg;
533    static SCSI_10_Byte_Command gWriteCommand = {
534	kScsiCmdWrite10, 0, 0, 0, 0, 0, 0, 0, 0, 0
535    };
536    SCSI_Sense_Data         senseData;
537    DeviceIdent scsiDevice;
538    int rtn_value;
539    long count;
540
541    scsiDevice.diReserved = 0;
542    scsiDevice.bus = bus;
543    scsiDevice.targetID = id;
544    scsiDevice.LUN = 0;
545
546    gWriteCommand.lbn4 = (block >> 24) & 0xFF;
547    gWriteCommand.lbn3 = (block >> 16) & 0xFF;
548    gWriteCommand.lbn2 = (block >> 8) & 0xFF;
549    gWriteCommand.lbn1 = block & 0xFF;
550
551    count = 1;
552    gWriteCommand.len2 = (count >> 8) & 0xFF;
553    gWriteCommand.len1 = count & 0xFF;
554
555    status = DoSCSICommand(
556		scsiDevice,
557		"\pWrite",
558		(SCSI_CommandPtr) &gWriteCommand,
559		(Ptr) address,
560		count * block_size,
561		scsiDirectionOut,
562		NULL,
563		&senseData,
564		errorText
565	);
566    if (status == noErr) {
567	rtn_value = 1;
568    } else {
569	rtn_value = 0;
570    }
571    return rtn_value;
572}
573
574
575int
576DoReadCapacity(UInt32 id, UInt32 bus, UInt32 *blockCount, UInt32 *blockSize)
577{
578    OSErr       status;
579    Str255      errorText;
580    static const SCSI_10_Byte_Command gCapacityCommand = {
581	kScsiCmdReadCapacity, 0, 0, 0, 0, 0, 0, 0, 0, 0
582    };
583    SCSI_Sense_Data senseData;
584    DeviceIdent     scsiDevice;
585    SCSI_Capacity_Data  capacityData;
586    UInt32      temp;
587    int rtn_value;
588
589    scsiDevice.diReserved = 0;
590    scsiDevice.bus = bus;
591    scsiDevice.targetID = id;
592    scsiDevice.LUN = 0;
593
594    CLEAR(capacityData);
595
596    status = DoSCSICommand(
597		scsiDevice,
598		"\pRead Capacity",
599		(SCSI_CommandPtr) &gCapacityCommand,
600		(Ptr) &capacityData,
601		sizeof (SCSI_Capacity_Data),
602		scsiDirectionIn,
603		NULL,
604		&senseData,
605		errorText
606		);
607
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;
613	*blockCount = temp;
614
615	temp = capacityData.len4;
616	temp = (temp << 8) | capacityData.len3;
617	temp = (temp << 8) | capacityData.len2;
618	temp = (temp << 8) | capacityData.len1;
619	*blockSize = temp;
620
621	rtn_value = 1;
622    } else {
623	rtn_value = 0;
624    }
625    return rtn_value;
626}
627
628
629int
630DoInquiry(UInt32 id, UInt32 bus, UInt32 *devType)
631{
632    OSErr       status;
633    Str255      errorText;
634    static const SCSI_6_Byte_Command gInquiryCommand = {
635	kScsiCmdInquiry, 0, 0, 0, kRequiredSCSIinquiryLength, 0
636    };
637    SCSI_Sense_Data senseData;
638    DeviceIdent     scsiDevice;
639    SCSI_Inquiry_Data  inquiryData;
640    UInt32      temp;
641    int rtn_value;
642
643    scsiDevice.diReserved = 0;
644    scsiDevice.bus = bus;
645    scsiDevice.targetID = id;
646    scsiDevice.LUN = 0;
647
648    CLEAR(inquiryData);
649
650    status = DoSCSICommand(
651		scsiDevice,
652		"\pInquiry",
653		(SCSI_CommandPtr) &gInquiryCommand,
654		(Ptr) &inquiryData,
655		kRequiredSCSIinquiryLength,
656		scsiDirectionIn,
657		NULL,
658		&senseData,
659		errorText
660		);
661
662    if (status == noErr) {
663	*devType = inquiryData.devType & kScsiDevTypeMask;
664	rtn_value = 1;
665    } else {
666	rtn_value = 0;
667    }
668    return rtn_value;
669}
670
671
672MEDIA
673SCSI_FindDevice(long dRefNum)
674{
675    SCSIDriverPB            pb;
676    OSErr                   status;
677    short                   targetID;
678
679    status = nsvErr;
680    if (AsyncSCSIPresent()) {
681	clear_memory((Ptr) &pb, sizeof pb);
682
683	pb.scsiPBLength = sizeof (SCSIDriverPB);
684	pb.scsiCompletion = NULL;
685	pb.scsiFlags = 0;
686	pb.scsiFunctionCode = SCSILookupRefNumXref;
687	pb.scsiDevice.bus = kNoDevice;  /* was *((long *) &pb.scsiDevice) = 0xFFFFFFFFL; */
688
689	do {
690	    status = SCSIAction((SCSI_PB *) &pb);
691
692	    if (status != noErr) {
693		break;
694	    } else if (pb.scsiDriver == dRefNum
695		    && pb.scsiDevice.bus != kNoDevice) {
696		return open_scsi_as_media(pb.scsiDevice.bus, pb.scsiDevice.targetID);
697
698	    } else {
699		pb.scsiDevice = pb.scsiNextDevice;
700	    }
701	}
702	while (pb.scsiDevice.bus != kNoDevice);
703    }
704    if (status == nsvErr) {
705	/*
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);
711	}
712    }
713     return 0;
714}
715
716
717#pragma mark -
718
719
720SCSI_MEDIA_ITERATOR
721new_scsi_iterator(void)
722{
723    return (SCSI_MEDIA_ITERATOR) new_media_iterator(sizeof(struct SCSI_media_iterator));
724}
725
726
727MEDIA_ITERATOR
728create_scsi_iterator(void)
729{
730    SCSI_MEDIA_ITERATOR a;
731
732    if (scsi_inited == 0) {
733	scsi_init();
734    }
735
736    if (scsi_mgr.exists == 0) {
737	return 0;
738    }
739
740    a = new_scsi_iterator();
741    if (a != 0) {
742	a->m.kind = scsi_mgr.kind;
743	a->m.state = kInit;
744	a->m.do_reset = reset_scsi_iterator;
745	a->m.do_step = step_scsi_iterator;
746	a->m.do_delete = delete_scsi_iterator;
747	a->bus_index = 0;
748	a->bus = 0;
749	a->id = 0;
750    }
751
752    return (MEDIA_ITERATOR) a;
753}
754
755
756void
757reset_scsi_iterator(MEDIA_ITERATOR m)
758{
759    SCSI_MEDIA_ITERATOR a;
760
761    a = (SCSI_MEDIA_ITERATOR) m;
762    if (a == 0) {
763	/* no media */
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) {
767	a->m.state = kReset;
768    }
769}
770
771
772char *
773step_scsi_iterator(MEDIA_ITERATOR m)
774{
775    SCSI_MEDIA_ITERATOR a;
776    char *result;
777
778    a = (SCSI_MEDIA_ITERATOR) m;
779    if (a == 0) {
780	/* no media */
781    } else if (a->m.kind != scsi_mgr.kind) {
782	/* wrong kind - XXX need to error here - this is an internal problem */
783    } else {
784	switch (a->m.state) {
785	case kInit:
786	    /* find # of buses - done in AllocatePB() out of scsi_init() */
787	    a->m.state = kReset;
788	    /* fall through to reset */
789	case kReset:
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;
794	    clear_linux_cache();
795	    /* fall through to iterate */
796	case kIterating:
797	    while (1) {
798		if (a->bus_index >= scsi_mgr.bus_count /* max bus id */) {
799		    break;
800		}
801		if (a->id == scsi_mgr.bus_list[a->bus_index].master_id) {
802		    /* next id */
803		    a->id += 1;
804		}
805		if (a->id > scsi_mgr.bus_list[a->bus_index].max_id) {
806		    a->bus_index += 1;
807		    a->bus = scsi_mgr.bus_list[a->bus_index].bus;
808		    a->id = 0 /* first device id */;
809		    continue;   /* try again */
810		}
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);
817		    } else {
818			// insure bus number in range
819			if (a->bus > 9) {
820			    free(result);
821			    result = NULL;
822			    break;
823			}
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);
828		    }
829		}
830
831		a->id += 1; /* next id */
832		return result;
833	    }
834	    a->m.state = kEnd;
835	    /* fall through to end */
836	case kEnd:
837	    mark_linux_cache_loaded();
838	default:
839	    break;
840	}
841    }
842    return 0 /* no entry */;
843}
844
845
846void
847delete_scsi_iterator(MEDIA_ITERATOR m)
848{
849    return;
850}
851
852
853#pragma mark -
854
855
856MEDIA
857open_linux_scsi_as_media(long index, int is_cdrom)
858{
859    MEDIA m;
860    long bus;
861    long id;
862
863    if (lookup_scsi_index(index, is_cdrom, &bus, &id) > 0) {
864	m = open_scsi_as_media(bus, id);
865    } else {
866	m = 0;
867    }
868
869    return m;
870}
871
872
873char *
874linux_old_scsi_name(long id)
875{
876    linux_scsi_name(kOriginalSCSIBusAdaptor, id);
877}
878
879
880char *
881linux_scsi_name(long bus, long id)
882{
883    char *result = 0;
884    long value;
885    int is_cdrom;
886    int unsure;
887    char *suffix;
888
889    /* name is sda, sdb, sdc, ...
890     * in order by buses and ids, but only count responding devices ...
891     */
892    if ((value = lookup_scsi_device(bus, id, &is_cdrom, &unsure)) >= 0) {
893	result = (char *) malloc(20);
894	if (result != NULL) {
895	    if (unsure) {
896		suffix = " ?";
897	    } else {
898		suffix = "";
899	    }
900	    if (is_cdrom) {
901		if (value > 9) {
902		    // too many CD's, give up
903		    free(result); result = NULL;
904		} else {
905		    snprintf(result, 20, "/dev/scd%c%s", '0' + value, suffix);
906		}
907	    } else {
908		if (value < 26) {
909		    snprintf(result, 20, "/dev/sd%c%s", 'a' + value, suffix);
910		} else {
911		    snprintf(result, 20, "/dev/sd%c%c%s",
912			    'a' + value / 26, 'a' + value % 26, suffix);
913		}
914	    }
915	}
916    }
917    return result;
918}
919
920
921void
922probe_all(void)
923{
924    MEDIA_ITERATOR iter;
925    char *name;
926
927    iter = create_scsi_iterator();
928    if (iter == 0) {
929	return;
930    }
931
932    printf("finding devices ");
933    fflush(stdout);
934    while ((name = step_media_iterator(iter)) != 0) {
935    	/* step does the probe for us */
936	printf(".");
937	fflush(stdout);
938	free(name);
939    }
940    delete_media_iterator(iter);
941    printf("\n");
942    fflush(stdout);
943}
944
945
946void
947probe_scsi_device(long bus, long id, int unsure)
948{
949    UInt32 devType;
950
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);
958    	}
959    }
960}
961
962
963long
964lookup_scsi_device(long bus, long id, int *is_cdrom, int *unsure)
965{
966    /* walk down list looking for bus and id ?
967     *
968     * only probe out of iterate (so always add in order)
969     * reset list if we reset the iterate
970     */
971    struct cache_item *item;
972    struct cache_item *next;
973    long result = -1;
974    int count = 0;
975
976    if (scsi_inited == 0) {
977	scsi_init();
978    }
979
980    while (1) {
981    	count++;
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;
987		break;
988	    }
989	}
990	if (count < 2 && result < 0) {
991	    probe_all();
992	} else {
993	    break;
994	}
995    };
996
997    return result;
998}
999
1000
1001/*
1002 * This has the same structure as lookup_scsi_device() except we are
1003 * matching on the value & type rather than the bus & id.
1004 */
1005long
1006lookup_scsi_index(long index, int is_cdrom, long *bus, long *id)
1007{
1008    struct cache_item *item;
1009    struct cache_item *next;
1010    long result = 0;
1011    int count = 0;
1012
1013    if (scsi_inited == 0) {
1014	scsi_init();
1015    }
1016
1017    while (1) {
1018    	count++;
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) {
1022		result = 1;
1023		*bus = item->bus;
1024		*id = item->id;
1025		break;
1026	    }
1027	}
1028	if (count < 2 && result == 0 && !linux_cache_loaded()) {
1029	    probe_all();
1030	} else {
1031	    break;
1032	}
1033    };
1034
1035    return result;
1036}
1037
1038
1039void
1040add_to_cache(long bus, long id, int is_cdrom, int unsure)
1041{
1042    struct cache_item *item;
1043
1044    item = malloc(sizeof(struct cache_item));
1045    if (item == NULL) {
1046	return;
1047    } else {
1048	item->bus = bus;
1049	item->id = id;
1050	item->is_cdrom = is_cdrom;
1051	item->unsure = unsure;
1052	if (is_cdrom) {
1053	    item->value = linux_order.next_cdrom;
1054	    linux_order.next_cdrom++;
1055	} else {
1056	    item->value = linux_order.next_disk;
1057	    linux_order.next_disk++;
1058	}
1059	item->next = 0;
1060    }
1061    if (linux_order.first == NULL) {
1062	linux_order.first = item;
1063	linux_order.last = item;
1064    } else {
1065	linux_order.last->next = item;
1066	linux_order.last = item;
1067    }
1068}
1069
1070
1071void
1072init_linux_cache(void)
1073{
1074    linux_order.first = NULL;
1075    clear_linux_cache();
1076}
1077
1078
1079void
1080clear_linux_cache(void)
1081{
1082    struct cache_item *item;
1083    struct cache_item *next;
1084
1085    for (item = linux_order.first; item != NULL; item = next) {
1086	next = item->next;
1087	free(item);
1088    }
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;
1095}
1096
1097
1098void
1099mark_linux_cache_loaded(void)
1100{
1101    linux_order.loaded = 1;
1102}
1103
1104
1105int
1106linux_cache_loaded(void)
1107{
1108    return linux_order.loaded;
1109}
1110