1//-----------------------------------------------------------------------------
2//	Includes
3//-----------------------------------------------------------------------------
4
5#include <string.h>
6#include <unistd.h>
7#include <getopt.h>
8#include <fcntl.h>
9#include <sysexits.h>
10#include <stdint.h>
11#include <IOKit/IOKitLib.h>
12#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
13#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
14#include <CoreFoundation/CoreFoundation.h>
15#include <IOKit/scsi/SCSITask.h>
16#include <IOKit/scsi/SCSICmds_REPORT_LUNS_Definitions.h>
17#include <IOKit/scsi/SCSICommandOperationCodes.h>
18#include <IOKit/scsi/SCSICmds_INQUIRY_Definitions.h>
19#include <CoreFoundation/CoreFoundation.h>
20
21#include "AppleSCSIEmulatorAdapterUC.h"
22#include "AppleSCSIEmulatorDefines.h"
23
24//-----------------------------------------------------------------------------
25//	Macros
26//-----------------------------------------------------------------------------
27
28#define DEBUG	0
29
30#define DEBUG_ASSERT_COMPONENT_NAME_STRING "Emulator"
31
32#if DEBUG
33#define PRINT(x)	printf x
34#else
35#define PRINT(x)
36#endif
37
38#if DEBUG
39#define DEBUG_ASSERT_MESSAGE(componentNameString,	\
40							 assertionString,		\
41							 exceptionLabelString,	\
42							 errorString,			\
43							 fileName,				\
44							 lineNumber,			\
45							 errorCode)				\
46DebugAssert(componentNameString,					\
47					   assertionString,				\
48					   exceptionLabelString,		\
49					   errorString,					\
50					   fileName,					\
51					   lineNumber,					\
52					   errorCode)					\
53
54static void
55DebugAssert ( const char *	componentNameString,
56			  const char *	assertionString,
57			  const char *	exceptionLabelString,
58			  const char *	errorString,
59			  const char *	fileName,
60			  long			lineNumber,
61			  int			errorCode )
62{
63
64	if ( ( assertionString != NULL ) && ( *assertionString != '\0' ) )
65		printf ( "Assertion failed: %s: %s\n", componentNameString, assertionString );
66	else
67		printf ( "Check failed: %s:\n", componentNameString );
68	if ( exceptionLabelString != NULL )
69		printf ( "	 %s\n", exceptionLabelString );
70	if ( errorString != NULL )
71		printf ( "	 %s\n", errorString );
72	if ( fileName != NULL )
73		printf ( "	 file: %s\n", fileName );
74	if ( lineNumber != 0 )
75		printf ( "	 line: %ld\n", lineNumber );
76	if ( errorCode != 0 )
77		printf ( "	 error: %d\n", errorCode );
78
79}
80
81#endif	/* DEBUG */
82
83#include <AssertMacros.h>
84
85
86//-----------------------------------------------------------------------------
87//	Constants
88//-----------------------------------------------------------------------------
89
90#define kAppleSCSIEmulatorAdapterClassString	"AppleSCSIEmulatorAdapter"
91#define kIOSCSIParallelInterfaceDeviceString	"IOSCSIParallelInterfaceDevice"
92#define kIOSCSITargetDeviceString				"IOSCSITargetDevice"
93#define kIOSCSIHierarchicalLogicalUnitString	"IOSCSIHierarchicalLogicalUnit"
94
95//-----------------------------------------------------------------------------
96//	Structures
97//-----------------------------------------------------------------------------
98
99#pragma pack(1)
100
101typedef struct EmulatorSCSIInquiryPage00Data
102{
103	UInt8							page00;
104	UInt8							page80;
105	UInt8							page83;
106} EmulatorSCSIInquiryPage00Data;
107
108typedef struct EmulatorSCSIInquiryPage83Data
109{
110	SCSICmd_INQUIRY_Page83_Identification_Descriptor	descriptor1;
111	UInt8												descriptor1bytes[7];
112	SCSICmd_INQUIRY_Page83_Identification_Descriptor	descriptor2;
113	UInt8												descriptor2bytes[15];
114} EmulatorSCSIInquiryPage83Data;
115
116typedef struct EmulatorSCSIInquiryPage80Data
117{
118	UInt8							serialBytes[31];
119} EmulatorSCSIInquiryPage80Data;
120
121typedef struct EmulatorSCSIInquiryPage00
122{
123	SCSICmd_INQUIRY_Page00_Header	header;
124	EmulatorSCSIInquiryPage00Data	data;
125} EmulatorSCSIInquiryPage00;
126
127typedef struct EmulatorSCSIInquiryPage80
128{
129	SCSICmd_INQUIRY_Page80_Header	header;
130	EmulatorSCSIInquiryPage80Data	data;
131} EmulatorSCSIInquiryPage80;
132
133typedef struct EmulatorSCSIInquiryPage83
134{
135	SCSICmd_INQUIRY_Page83_Header	header;
136	EmulatorSCSIInquiryPage83Data	data;
137} EmulatorSCSIInquiryPage83;
138
139#pragma options align=reset
140
141
142//-----------------------------------------------------------------------------
143//	Globals
144//-----------------------------------------------------------------------------
145
146SCSICmd_INQUIRY_StandardData gInquiryData =
147{
148	kINQUIRY_PERIPHERAL_TYPE_DirectAccessSBCDevice,	// PERIPHERAL_DEVICE_TYPE
149	0,	// RMB;
150	5,	// VERSION
151	2,	// RESPONSE_DATA_FORMAT
152	sizeof ( SCSICmd_INQUIRY_StandardData ) - 5,	// ADDITIONAL_LENGTH
153	0,	// SCCSReserved
154	0,	// flags1
155	0,	// flags2
156	"APPLE",
157	"SCSI Emulator",
158	"1.0",
159};
160
161static EmulatorSCSIInquiryPage00 gInquiryPage00Data =
162{
163	0,											// PERIPHERAL_DEVICE_TYPE
164	kINQUIRY_Page00_PageCode,					// PAGE_CODE
165	0,											// RESERVED
166	sizeof ( EmulatorSCSIInquiryPage00Data ),	// PAGE_LENGTH
167	kINQUIRY_Page00_PageCode,
168	kINQUIRY_Page80_PageCode,
169	kINQUIRY_Page83_PageCode
170};
171
172static EmulatorSCSIInquiryPage80 gInquiryPage80Data =
173{
174	0,												// PERIPHERAL_DEVICE_TYPE
175	kINQUIRY_Page80_PageCode,						// PAGE_CODE
176	0,												// RESERVED
177	sizeof ( EmulatorSCSIInquiryPage80Data ) + 1,	// PAGE_LENGTH
178	'A',
179	'P',
180	'P',
181	'L',
182	'E',
183	' ',
184	'V',
185	'i',
186	'r',
187	't',
188	'u',
189	'a',
190	'l',
191	' ',
192	'L',
193	'U',
194	'N',
195	'0',
196	0
197};
198
199static EmulatorSCSIInquiryPage83 gInquiryPage83Data =
200{
201	0,											// PERIPHERAL_DEVICE_TYPE
202	kINQUIRY_Page83_PageCode,					// PAGE_CODE
203	0,											// RESERVED
204	sizeof ( EmulatorSCSIInquiryPage83Data ),	// PAGE_LENGTH
205	{
206		{													// Descriptor1
207			kINQUIRY_Page83_CodeSetBinaryData,				// CODE_SET
208			kINQUIRY_Page83_AssociationTargetDevice | kINQUIRY_Page83_IdentifierTypeFCNameIdentifier, // IDENTIFIER_TYPE
209			0x00,											// Reserved
210			0x08,											// IDENTIFIER_LENGTH
211			0x50											// IDENTIFIER
212		},
213		{
214			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
215		},
216		{													// Descriptor2
217			kINQUIRY_Page83_CodeSetBinaryData,				// CODE_SET
218			kINQUIRY_Page83_IdentifierTypeFCNameIdentifier, // IDENTIFIER_TYPE
219			0x00,											// Reserved
220			0x10,											// IDENTIFIER_LENGTH
221			0x60											// IDENTIFIER
222		},
223		{
224			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
225		}
226	},
227};
228
229
230//-----------------------------------------------------------------------------
231//		Prototypes
232//-----------------------------------------------------------------------------
233
234static void
235CreateTargetLUN (
236	SCSITargetIdentifier	targetID,
237	SCSILogicalUnitNumber	logicalUnit,
238	UInt64					capacity,
239	boolean_t				unique );
240
241static void
242DestroyTargetLUN (
243	SCSITargetIdentifier 	targetID,
244	SCSILogicalUnitNumber 	logicalUnit );
245
246static void
247DestroyTarget (
248	SCSITargetIdentifier 	targetID );
249
250static io_object_t
251GetController ( void );
252
253static void
254PrintUsage ( void );
255
256static void
257PrintController ( io_object_t controller );
258
259static void
260PrintTarget ( io_object_t target );
261
262static void
263PrintLogicalUnit ( io_object_t logicalUnit );
264
265static void
266ReportInventory ( void );
267
268
269//-----------------------------------------------------------------------------
270//		main - Our main entry point
271//-----------------------------------------------------------------------------
272
273int
274main ( int argc, const char * argv[] )
275{
276
277	boolean_t		create		= false;
278	boolean_t		destroy		= false;
279	boolean_t		inventory	= false;
280	boolean_t		unique		= true;
281	int64_t			targetID	= -1;
282	int64_t			lun			= -1;
283	uint64_t		size		= 0;
284	char			c;
285
286	static struct option long_options [ ] =
287	{
288		{ "target",			required_argument,	0, 't' },
289		{ "lun",			required_argument,	0, 'l' },
290		{ "size",			required_argument,	0, 's' },
291		{ "inventory",		no_argument,		0, 'i' },
292        { "create",			no_argument,		0, 'c' },
293		{ "destroy",		no_argument,		0, 'd' },
294		{ "help",			no_argument,		0, 'h' },
295		{ "nounique",		no_argument,		0, 'n' },
296		{ 0, 0, 0, 0 }
297	};
298
299	while ( ( c = getopt_long ( argc, ( char * const * ) argv, "t:l:s:icdhn?", long_options, NULL ) ) != -1 )
300	{
301
302		switch ( c )
303		{
304
305			case 't':
306			{
307
308				targetID = strtoull ( optarg, ( char ** ) NULL, 10 );
309				if ( ( targetID > 255 ) || ( targetID == kInitiatorID ) )
310				{
311					PRINT ( ( "Invalid targetID.\n" ) );
312					PrintUsage ( );
313					exit ( EX_USAGE );
314				}
315
316			}
317			break;
318
319			case 'l':
320			{
321
322				lun = strtoull ( optarg, ( char ** ) NULL, 10 );
323				if ( ( lun > 16383 ) || ( lun == 0 ) )
324				{
325					PRINT ( ( "Invalid LUN.\n" ) );
326					PrintUsage ( );
327					exit ( EX_USAGE );
328				}
329
330			}
331			break;
332
333			case 's':
334			{
335
336				char *	expr;
337
338				size = strtoull ( optarg, &expr, 10 );
339
340				switch ( *expr )
341				{
342
343					case 'k':
344						size *= 1 << 10;
345						break;
346
347					case 'm':
348						size *= 1 << 20;
349						break;
350
351					case 'g':
352						size *= 1 << 30;
353						break;
354
355					default:
356						break;
357
358				}
359
360				if ( size % 512 )
361				{
362					PRINT ( ( "Invalid byte count. Must be a multiple of 512 bytes\n" ) );
363					PrintUsage ( );
364					exit ( EX_USAGE );
365				}
366
367			}
368			break;
369
370			case 'i':
371			{
372
373				if ( ( create ) || ( destroy ) )
374				{
375					PRINT ( ( "Create, Destroy, and Inventory are mutually exclusive.\n" ) );
376					PrintUsage ( );
377					exit ( EX_USAGE );
378				}
379
380				inventory = true;
381
382			}
383			break;
384
385			case 'c':
386			{
387
388				if ( ( inventory ) || ( destroy ) )
389				{
390					PRINT ( ( "Create, Destroy, and Inventory are mutually exclusive.\n" ) );
391					PrintUsage ( );
392					exit ( EX_USAGE );
393				}
394
395				create = true;
396
397			}
398			break;
399
400			case 'd':
401			{
402
403				if ( ( create ) || ( inventory ) )
404				{
405					PRINT ( ( "Create, Destroy, and Inventory are mutually exclusive.\n" ) );
406					PrintUsage ( );
407					exit ( EX_USAGE );
408				}
409
410				destroy = true;
411
412			}
413			break;
414
415			case 'n':
416			{
417				unique = false;
418			}
419			break;
420
421			case 'h':
422			default:
423			{
424
425				PrintUsage ( );
426				exit ( EX_USAGE );
427
428			}
429			break;
430
431		}
432
433	}
434
435	if ( inventory )
436	{
437
438		ReportInventory ( );
439		exit ( 0 );
440
441	}
442
443	if ( create )
444	{
445
446		if ( ( targetID == -1 ) || ( lun == -1 ) || ( size == 0 ) )
447		{
448
449			PrintUsage ( );
450			exit ( EX_USAGE );
451
452		}
453
454		CreateTargetLUN ( targetID, lun, size, unique );
455
456	}
457
458	else if ( destroy )
459	{
460
461		if ( targetID == -1 )
462		{
463
464			PrintUsage ( );
465			exit ( EX_USAGE );
466
467		}
468
469		if ( lun == -1 )
470		{
471			DestroyTarget ( targetID );
472		}
473
474		else
475		{
476			DestroyTargetLUN ( targetID, lun );
477		}
478
479	}
480
481	else
482	{
483
484		PrintUsage ( );
485		exit ( EX_USAGE );
486
487	}
488
489	return 0;
490
491}
492
493
494//-----------------------------------------------------------------------------
495//		CreateTargetLUN - Creates a Logical Unit attached to a target.
496//-----------------------------------------------------------------------------
497
498static void
499CreateTargetLUN (
500	SCSITargetIdentifier	targetID,
501	SCSILogicalUnitNumber	logicalUnit,
502	UInt64					capacity,
503	boolean_t				unique )
504{
505
506	io_object_t		controller = IO_OBJECT_NULL;
507
508	PRINT ( ( "CreateTargetLUN, targetID = %qd, logicalUnit = %qd, capacity = %qd\n", targetID, logicalUnit, capacity ) );
509
510	controller = GetController ( );
511	if ( controller != IO_OBJECT_NULL )
512	{
513
514		io_connect_t	connection 	= IO_OBJECT_NULL;
515		IOReturn		status		= kIOReturnSuccess;
516
517		status = IOServiceOpen (
518			controller,
519			mach_task_self ( ),
520			kSCSIEmulatorAdapterUserClientConnection,
521			&connection );
522
523		if ( status == kIOReturnSuccess )
524		{
525
526			EmulatorTargetParamsStruct		target;
527			EmulatorLUNParamsStruct			lun;
528			size_t							outCount	= 0;
529			EmulatorSCSIInquiryPage80		page80		= gInquiryPage80Data;
530			EmulatorSCSIInquiryPage83		page83		= gInquiryPage83Data;
531			char							serial[32];
532
533			bzero ( &target, sizeof ( EmulatorTargetParamsStruct ) );
534			bzero ( &lun, sizeof ( EmulatorLUNParamsStruct ) );
535
536			lun.logicalUnit 				= logicalUnit;
537			lun.capacity 					= capacity;
538
539			lun.inquiryData 				= ( mach_vm_address_t ) ( uintptr_t ) &gInquiryData;
540			lun.inquiryPage00Data 			= ( mach_vm_address_t ) ( uintptr_t ) &gInquiryPage00Data;
541			lun.inquiryPage80Data 			= ( mach_vm_address_t ) ( uintptr_t ) &page80;
542			lun.inquiryPage83Data 			= ( mach_vm_address_t ) ( uintptr_t ) &page83;
543
544			lun.inquiryDataLength			= sizeof ( gInquiryData );
545			lun.inquiryPage00DataLength 	= sizeof ( gInquiryPage00Data );
546			lun.inquiryPage80DataLength 	= sizeof ( page80 );
547			lun.inquiryPage83DataLength 	= sizeof ( page83 );
548
549			target.targetID 				= targetID;
550			target.lun						= lun;
551
552			PRINT ( ( "lun.inquiryData = %p\n", &gInquiryData ) );
553			PRINT ( ( "lun.inquiryData = %p\n", &gInquiryPage00Data ) );
554			PRINT ( ( "lun.inquiryData = %p\n", &page80 ) );
555			PRINT ( ( "lun.inquiryData = %p\n", &page83 ) );
556
557			PRINT ( ( "gInquiryPage00Data = 0x%02x 0x%02x 0x%02x 0x%02x  0x%02x 0x%02x 0x%02x\n",
558					   gInquiryPage00Data.header.PERIPHERAL_DEVICE_TYPE,
559					   gInquiryPage00Data.header.PAGE_CODE,
560					   gInquiryPage00Data.header.RESERVED,
561					   gInquiryPage00Data.header.PAGE_LENGTH,
562					   gInquiryPage00Data.data.page00,
563					   gInquiryPage00Data.data.page80,
564					   gInquiryPage00Data.data.page83 ) );
565
566			PRINT ( ( "sizeof ( gInquiryData ) = %ld\n", sizeof ( gInquiryData ) ) );
567			PRINT ( ( "sizeof ( gInquiryPage00Data ) = %ld\n", sizeof ( gInquiryPage00Data ) ) );
568			PRINT ( ( "sizeof ( page80 ) = %ld\n", sizeof ( page80 ) ) );
569			PRINT ( ( "sizeof ( page83 ) = %ld\n", sizeof ( page83 ) ) );
570			PRINT ( ( "sizeof ( EmulatorTargetParamsStruct ) = %ld\n", sizeof ( EmulatorTargetParamsStruct ) ) );
571			PRINT ( ( "sizeof ( EmulatorLUNParamsStruct ) = %ld\n", sizeof ( EmulatorLUNParamsStruct ) ) );
572
573			// Fill in LUN information for page 80.
574			snprintf ( serial, 32, "APPLE Virtual LUN %qd", logicalUnit );
575			bcopy ( serial, ( char * ) &page80.header.PRODUCT_SERIAL_NUMBER, 32 );
576
577			if ( unique )
578			{
579
580				int		fd = -1;
581				char	randomBytes[15];
582				int		amount;
583
584				// Get some random bytes from /dev/random.
585				fd = open ( "/dev/random", O_RDONLY, 0 );
586				if ( fd == -1 )
587				{
588
589					PRINT ( ( "Open /dev/random failed\n" ) );
590					exit ( -1 );
591
592				}
593
594				amount = read ( fd, randomBytes, sizeof ( randomBytes ) );
595				if ( amount != sizeof ( randomBytes ) )
596				{
597
598					PRINT ( ( "Reading from /dev/random failed\n" ) );
599					exit ( -1 );
600
601				}
602
603				// Make sure that the page 83 data is unique for this LUN.
604				bcopy ( randomBytes, page83.data.descriptor2bytes, sizeof ( page83.data.descriptor2bytes ) );
605
606				close ( fd );
607
608			}
609
610			IOConnectCallStructMethod (
611				connection,
612				kUserClientCreateLUN,
613				&target,
614				sizeof ( EmulatorTargetParamsStruct ),
615				NULL,
616				&outCount );
617
618			IOServiceClose ( connection );
619
620		}
621
622		IOObjectRelease ( controller );
623
624	}
625
626}
627
628
629//-----------------------------------------------------------------------------
630//		DestroyTargetLUN - Destroys a Logical Unit attached to a target.
631//-----------------------------------------------------------------------------
632
633static void
634DestroyTargetLUN (
635	SCSITargetIdentifier 	targetID,
636	SCSILogicalUnitNumber 	logicalUnit )
637{
638
639	io_object_t		controller = IO_OBJECT_NULL;
640
641	PRINT ( ( "DestroyTargetLUN, targetID = %qd, logicalUnit = %qd\n", targetID, logicalUnit ) );
642
643	controller = GetController ( );
644	if ( controller != IO_OBJECT_NULL )
645	{
646
647		io_connect_t	connection 	= IO_OBJECT_NULL;
648		IOReturn		status		= kIOReturnSuccess;
649
650		status = IOServiceOpen (
651			controller,
652			mach_task_self ( ),
653			kSCSIEmulatorAdapterUserClientConnection,
654			&connection );
655
656		if ( status == kIOReturnSuccess )
657		{
658
659			uint32_t	outCount = 0;
660			uint64_t	params[2];
661
662			params[0] = targetID;
663			params[1] = logicalUnit;
664
665			IOConnectCallScalarMethod (
666				connection,
667				kUserClientDestroyLUN,
668				( const uint64_t * ) params,
669				2,
670				NULL,
671				&outCount );
672
673			IOServiceClose ( connection );
674
675		}
676
677		IOObjectRelease ( controller );
678
679	}
680
681}
682
683
684//-----------------------------------------------------------------------------
685//		DestroyTarget - Destroys a target.
686//-----------------------------------------------------------------------------
687
688static void
689DestroyTarget (
690	SCSITargetIdentifier 	targetID )
691{
692
693	io_object_t		controller = IO_OBJECT_NULL;
694
695	PRINT ( ( "DestroyTarget, targetID = %qd\n", targetID ) );
696
697	controller = GetController ( );
698	if ( controller != IO_OBJECT_NULL )
699	{
700
701		io_connect_t	connection 	= IO_OBJECT_NULL;
702		IOReturn		status		= kIOReturnSuccess;
703
704		status = IOServiceOpen (
705			controller,
706			mach_task_self ( ),
707			kSCSIEmulatorAdapterUserClientConnection,
708			&connection );
709
710		if ( status == kIOReturnSuccess )
711		{
712
713			uint32_t	outCount = 0;
714			uint64_t	params[1];
715
716			params[0] = targetID;
717
718			IOConnectCallScalarMethod (
719				connection,
720				kUserClientDestroyTarget,
721				( const uint64_t * ) params,
722				1,
723				NULL,
724				&outCount );
725
726			IOServiceClose ( connection );
727
728		}
729
730		IOObjectRelease ( controller );
731
732	}
733
734}
735
736
737//-----------------------------------------------------------------------------
738//		GetController - Gets the controller object.
739//-----------------------------------------------------------------------------
740
741static io_object_t
742GetController ( void )
743{
744
745	io_object_t		controller = IO_OBJECT_NULL;
746
747	controller = IOServiceGetMatchingService (
748		kIOMasterPortDefault,
749		IOServiceMatching ( kAppleSCSIEmulatorAdapterClassString ) );
750
751	return controller;
752
753}
754
755
756//-----------------------------------------------------------------------------
757//		ReportInventory - Reports the target/lun inventory
758//-----------------------------------------------------------------------------
759
760static void
761ReportInventory ( void )
762{
763
764	io_object_t		controller = IO_OBJECT_NULL;
765
766	controller = GetController ( );
767	if ( controller != IO_OBJECT_NULL )
768	{
769
770		IOReturn				result		= kIOReturnSuccess;
771		io_iterator_t			iterator	= IO_OBJECT_NULL;
772		io_registry_entry_t		child		= IO_OBJECT_NULL;
773
774		PrintController ( controller );
775
776		// Look for devices on this bus
777		result = IORegistryEntryCreateIterator ( controller, kIOServicePlane, kNilOptions, &iterator );
778		if ( result != kIOReturnSuccess )
779		{
780			goto ErrorExit;
781		}
782
783		child = IOIteratorNext ( iterator );
784
785		while ( child != IO_OBJECT_NULL )
786		{
787
788			if ( IOObjectConformsTo ( child, kIOSCSIParallelInterfaceDeviceString ) )
789			{
790
791				io_iterator_t	iterator2 = IO_OBJECT_NULL;
792
793				result = IORegistryEntryCreateIterator ( child, kIOServicePlane, kNilOptions, &iterator2 );
794				if ( result == kIOReturnSuccess )
795				{
796
797					io_registry_entry_t		grandchild = IO_OBJECT_NULL;
798
799					grandchild = IOIteratorNext ( iterator2 );
800
801					while ( grandchild != IO_OBJECT_NULL )
802					{
803
804						if ( IOObjectConformsTo ( grandchild, kIOSCSITargetDeviceString ) )
805						{
806
807							io_iterator_t	iterator3 = IO_OBJECT_NULL;
808
809							PrintTarget ( grandchild );
810
811							result = IORegistryEntryCreateIterator ( grandchild, kIOServicePlane, kNilOptions, &iterator3 );
812							if ( result == kIOReturnSuccess )
813							{
814
815								io_registry_entry_t		greatgrandchild = IO_OBJECT_NULL;
816
817								greatgrandchild = IOIteratorNext ( iterator3 );
818
819								while ( greatgrandchild != IO_OBJECT_NULL )
820								{
821
822									if ( IOObjectConformsTo ( greatgrandchild, kIOSCSIHierarchicalLogicalUnitString ) )
823									{
824
825										PrintLogicalUnit ( greatgrandchild );
826
827									}
828
829									IOObjectRelease ( greatgrandchild );
830									greatgrandchild = IOIteratorNext ( iterator3 );
831
832								}
833
834								IOObjectRelease ( iterator3 );
835
836							}
837
838							printf ( "-------------------------------------------------------------------------------\n" );
839
840						}
841
842						IOObjectRelease ( grandchild );
843						grandchild = IOIteratorNext ( iterator2 );
844
845					}
846
847					IOObjectRelease ( iterator2 );
848
849				}
850
851			}
852
853			IOObjectRelease ( child );
854			child = IOIteratorNext ( iterator );
855
856		}
857
858		IOObjectRelease ( iterator );
859
860	}
861
862	else
863	{
864
865		printf ( "No AppleSCSIEmulatorAdapter class found, please make sure the kext is loaded and try again.\n" );
866
867	}
868
869ErrorExit:
870
871
872	IOObjectRelease ( controller );
873
874}
875
876
877//-----------------------------------------------------------------------------
878//		PrintController - Dump controller information
879//-----------------------------------------------------------------------------
880
881static void
882PrintController ( io_object_t controller )
883{
884
885	CFNumberRef			number;
886	CFDictionaryRef		dict;
887
888	printf ( "Controller\n" );
889
890	dict = ( CFDictionaryRef ) IORegistryEntrySearchCFProperty ( controller, kIOServicePlane, CFSTR ( kIOPropertyProtocolCharacteristicsKey ), kCFAllocatorDefault, kIORegistryIterateRecursively );
891	if ( dict != NULL )
892	{
893
894		number = ( CFNumberRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSIDomainIdentifierKey ) );
895		if ( number != NULL )
896		{
897
898			int		domain = 0;
899
900			CFNumberGetValue ( number, kCFNumberIntType, &domain );
901			printf ( "\tDomain ID: %d\n", domain );
902
903		}
904
905		CFRelease ( dict );
906		dict = NULL;
907
908	}
909
910}
911
912
913//-----------------------------------------------------------------------------
914//		PrintTarget - Dump target information
915//-----------------------------------------------------------------------------
916
917static void
918PrintTarget ( io_object_t target )
919{
920
921	SCSITargetIdentifier	targetID = 0;
922	CFNumberRef				number;
923	CFDictionaryRef			dict;
924
925	printf ( "-------------------------------------------------------------------------------\n" );
926
927	dict = ( CFDictionaryRef ) IORegistryEntrySearchCFProperty ( target, kIOServicePlane, CFSTR ( kIOPropertyProtocolCharacteristicsKey ), kCFAllocatorDefault, kIORegistryIterateRecursively );
928	if ( dict != NULL )
929	{
930
931		number = ( CFNumberRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSITargetIdentifierKey ) );
932		if ( number != NULL )
933		{
934
935			CFNumberGetValue ( number, kCFNumberSInt64Type, &targetID );
936			printf ( "\nTargetDevice@%qd\n", targetID );
937
938		}
939
940		CFRelease ( dict );
941		dict = NULL;
942
943	}
944
945}
946
947
948//-----------------------------------------------------------------------------
949//		PrintLogicalUnit - Dump logical unit information
950//-----------------------------------------------------------------------------
951
952static void
953PrintLogicalUnit ( io_object_t logicalUnit )
954{
955
956	CFNumberRef				number	= NULL;
957	CFDictionaryRef			dict	= NULL;
958	CFStringRef				string	= NULL;
959
960	dict = ( CFDictionaryRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertyProtocolCharacteristicsKey ), kCFAllocatorDefault, kIORegistryIterateRecursively );
961	if ( dict != NULL )
962	{
963
964#if	USE_LUN_BYTES
965
966		CFDataRef	data = NULL;
967
968		data = ( CFDataRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSILogicalUnitBytesKey ) );
969		if ( data != NULL )
970		{
971
972			const UInt8 *	ptr = ( UInt8 * ) CFDataGetBytePtr ( data );
973			printf ( "\nLogicalUnit: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
974					 ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7] );
975
976		}
977
978#else
979
980		number = ( CFNumberRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOPropertySCSILogicalUnitNumberKey ) );
981		if ( number != NULL )
982		{
983
984			UInt64	LUN = 0;
985
986			CFNumberGetValue ( number, kCFNumberLongLongType, &LUN );
987
988			printf ( "\nLogicalUnit: 0x%qd\n", LUN );
989
990		}
991
992#endif	/* USE_LUN_BYTES */
993
994		CFRelease ( dict );
995		dict = NULL;
996
997	}
998
999	// Describe the device type
1000	string = ( CFStringRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIVendorIdentification ), kCFAllocatorDefault, kIORegistryIterateRecursively );
1001	if ( string != NULL )
1002	{
1003
1004		printf ( "\tVendor: %s\n", CFStringGetCStringPtr ( string, kCFStringEncodingMacRoman ) );
1005		CFRelease ( string );
1006		string = NULL;
1007
1008	}
1009
1010	// Describe the device type
1011	string = ( CFStringRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIProductIdentification ), kCFAllocatorDefault, kIORegistryIterateRecursively );
1012	if ( string != NULL )
1013	{
1014
1015		printf ( "\tProduct: %s\n", CFStringGetCStringPtr ( string, kCFStringEncodingMacRoman ) );
1016		CFRelease ( string );
1017		string = NULL;
1018
1019	}
1020
1021	// Describe the device type
1022	string = ( CFStringRef ) IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIProductRevisionLevel ), kCFAllocatorDefault, kIORegistryIterateRecursively );
1023	if ( string != NULL )
1024	{
1025
1026		printf ( "\tProduct Revision Level: %s\n", CFStringGetCStringPtr ( string, kCFStringEncodingMacRoman ) );
1027		CFRelease ( string );
1028		string = NULL;
1029
1030	}
1031
1032
1033	// Describe the device type
1034	number = ( CFNumberRef )IORegistryEntrySearchCFProperty ( logicalUnit, kIOServicePlane, CFSTR ( kIOPropertySCSIPeripheralDeviceType ), kCFAllocatorDefault, kIORegistryIterateRecursively );
1035	if ( number != NULL )
1036	{
1037
1038		int		pdt = 0;
1039
1040		CFNumberGetValue ( number, kCFNumberIntType, &pdt );
1041		printf ( "\tPeripheral Device Type: %d\n", pdt );
1042		CFRelease ( number );
1043		number = NULL;
1044
1045	}
1046
1047}
1048
1049
1050//-----------------------------------------------------------------------------
1051//		PrintUsage - Prints usage string
1052//-----------------------------------------------------------------------------
1053
1054static void
1055PrintUsage ( void )
1056{
1057
1058	printf ( "Usage: emulator [--create, -c] [--destroy, -d] [--inventory, -i] [--target, -t] [--lun, -l] [--unique, -u] [--size, -s]\n" );
1059	printf ( "       --create and --destroy are mutually exclusive\n" );
1060	printf ( "       --target accepts targetIDs in the rang of [0...14][16...255]. ID 15 is reserved for the initiator.\n" );
1061	printf ( "       --lun accepts LUNs in the range of [1...16383] inclusive.\n" );
1062	printf ( "       --size can be in bytes, kilobytes, megabytes, or gigabytes, suffix usage similar to dd\n" );
1063	printf ( "       --unique is used to specify if the logical unit being created has a unique identifier in INQUIRY VPD Page 83h. If --unique is not used, the default (shared) INQUIRY VPD Page 83h identifier will be used\n" );
1064	fflush ( stdout );
1065
1066}