xref: /netbsd-src/external/bsd/pdisk/dist/SCSI_media.c (revision 48a628ae0434c4247b560ad8f2eb1dc06d0dd070)
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)  ((int16_t) (~(x) - 32))
42 
43 
44 /*
45  * Types
46  */
47 typedef struct SCSI_media *SCSI_MEDIA;
48 
49 struct SCSI_media {
50     struct media    m;
51     long            bus;
52     long            id;
53 };
54 
55 struct bus_entry {
56     long    bus;
57     long    sort_value;
58     long    max_id;
59     long    master_id;
60 };
61 
62 struct SCSI_manager {
63     long        exists;
64     long        kind;
65     long        bus_count;
66     struct bus_entry *bus_list;
67 };
68 
69 typedef struct SCSI_media_iterator *SCSI_MEDIA_ITERATOR;
70 
71 struct SCSI_media_iterator {
72     struct media_iterator   m;
73     long                    bus_index;
74     long                    bus;
75     long                    id;
76 };
77 
78 struct 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 
86 struct 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  */
99 enum {
100     kNoDevice = 0x00FF
101 };
102 
103 enum {
104     kRequiredSCSIinquiryLength = 36
105 };
106 
107 
108 /*
109  * Global Variables
110  */
111 static long scsi_inited = 0;
112 static struct SCSI_manager scsi_mgr;
113 static struct linux_order_cache linux_order;
114 
115 
116 /*
117  * Forward declarations
118  */
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, uint32_t count, void *address);
123 long write_scsi_media(MEDIA m, long long offset, uint32_t 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);
148 
149 
150 /*
151  * Routines
152  */
153 int
AsyncSCSIPresent(void)154 AsyncSCSIPresent(void)
155 {
156     return (TrapAvailable(_SCSIAtomic));
157 }
158 
159 
160 void
scsi_init(void)161 scsi_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 
207 void
fill_bus_entry(struct bus_entry * entry,long bus)208 fill_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 
256 int
bus_entry_compare(const void * a,const void * b)257 bus_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 
272 SCSI_MEDIA
new_scsi_media(void)273 new_scsi_media(void)
274 {
275     return (SCSI_MEDIA) new_media(sizeof(struct SCSI_media));
276 }
277 
278 
279 MEDIA
open_old_scsi_as_media(long device)280 open_old_scsi_as_media(long device)
281 {
282     return open_scsi_as_media(kOriginalSCSIBusAdaptor, device);
283 }
284 
285 
286 MEDIA
open_scsi_as_media(long bus,long device)287 open_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 
322 long
read_scsi_media(MEDIA m,long long offset,uint32_t count,void * address)323 read_scsi_media(MEDIA m, long long offset, uint32_t count, void *address)
324 {
325     SCSI_MEDIA a;
326     long rtn_value;
327     long block;
328     long block_count;
329     long block_size;
330     uint8_t *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 
367 long
write_scsi_media(MEDIA m,long long offset,uint32_t count,void * address)368 write_scsi_media(MEDIA m, long long offset, uint32_t count, void *address)
369 {
370     SCSI_MEDIA a;
371     long rtn_value;
372     long block;
373     long block_count;
374     long block_size;
375     uint8_t *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 
410 long
close_scsi_media(MEDIA m)411 close_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 
427 long
os_reload_scsi_media(MEDIA m)428 os_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 
438 int
DoTestUnitReady(UInt8 targetID,int bus)439 DoTestUnitReady(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 
478 int
SCSI_ReadBlock(UInt32 id,UInt32 bus,UInt32 block_size,UInt32 block,UInt8 * address)479 SCSI_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 
527 int
SCSI_WriteBlock(UInt32 id,UInt32 bus,UInt32 block_size,UInt32 block,UInt8 * address)528 SCSI_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 
575 int
DoReadCapacity(UInt32 id,UInt32 bus,UInt32 * blockCount,UInt32 * blockSize)576 DoReadCapacity(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 
629 int
DoInquiry(UInt32 id,UInt32 bus,UInt32 * devType)630 DoInquiry(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 
672 MEDIA
SCSI_FindDevice(long dRefNum)673 SCSI_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 
720 SCSI_MEDIA_ITERATOR
new_scsi_iterator(void)721 new_scsi_iterator(void)
722 {
723     return (SCSI_MEDIA_ITERATOR) new_media_iterator(sizeof(struct SCSI_media_iterator));
724 }
725 
726 
727 MEDIA_ITERATOR
create_scsi_iterator(void)728 create_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 
756 void
reset_scsi_iterator(MEDIA_ITERATOR m)757 reset_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 
772 char *
step_scsi_iterator(MEDIA_ITERATOR m)773 step_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 
846 void
delete_scsi_iterator(MEDIA_ITERATOR m)847 delete_scsi_iterator(MEDIA_ITERATOR m)
848 {
849     return;
850 }
851 
852 
853 #pragma mark -
854 
855 
856 MEDIA
open_linux_scsi_as_media(long index,int is_cdrom)857 open_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 
873 char *
linux_old_scsi_name(long id)874 linux_old_scsi_name(long id)
875 {
876     linux_scsi_name(kOriginalSCSIBusAdaptor, id);
877 }
878 
879 
880 char *
linux_scsi_name(long bus,long id)881 linux_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 
921 void
probe_all(void)922 probe_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 
946 void
probe_scsi_device(long bus,long id,int unsure)947 probe_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 
963 long
lookup_scsi_device(long bus,long id,int * is_cdrom,int * unsure)964 lookup_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  */
1005 long
lookup_scsi_index(long index,int is_cdrom,long * bus,long * id)1006 lookup_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 
1039 void
add_to_cache(long bus,long id,int is_cdrom,int unsure)1040 add_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 
1071 void
init_linux_cache(void)1072 init_linux_cache(void)
1073 {
1074     linux_order.first = NULL;
1075     clear_linux_cache();
1076 }
1077 
1078 
1079 void
clear_linux_cache(void)1080 clear_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 
1098 void
mark_linux_cache_loaded(void)1099 mark_linux_cache_loaded(void)
1100 {
1101     linux_order.loaded = 1;
1102 }
1103 
1104 
1105 int
linux_cache_loaded(void)1106 linux_cache_loaded(void)
1107 {
1108     return linux_order.loaded;
1109 }
1110