1/*
2  File: AppleSCSIPDT00Emulator.cpp
3
4  Contains:
5
6  Version: 1.0.0
7
8  Copyright: Copyright (c) 2007 by Apple Inc., All Rights Reserved.
9
10Disclaimer:IMPORTANT:  This Apple software is supplied to you by Apple Inc.
11("Apple") in consideration of your agreement to the following terms, and your use,
12installation, modification or redistribution of this Apple software constitutes acceptance
13of these terms.  If you do not agree with these terms, please do not use, install, modify or
14redistribute this Apple software.
15
16In consideration of your agreement to abide by the following terms, and subject
17to these terms, Apple grants you a personal, non-exclusive license, under Apple's
18copyrights in this original Apple software (the "Apple Software"), to use, reproduce,
19modify and redistribute the Apple Software, with or without modifications, in source and/or
20binary forms; provided that if you redistribute the Apple Software in its entirety
21and without modifications, you must retain this notice and the following text
22and disclaimers in all such redistributions of the Apple Software.  Neither the
23name, trademarks, service marks or logos of Apple Inc. may be used to
24endorse or promote products derived from the Apple Software without specific prior
25written permission from Apple.  Except as expressly stated in this notice, no
26other rights or licenses, express or implied, are granted by Apple herein,
27including but not limited to any patent rights that may be infringed by your derivative
28works or by other works in which the Apple Software may be incorporated.
29
30The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES,
31EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
32MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE
33OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE
34BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
37REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
38AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT
39LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40*/
41
42
43//-----------------------------------------------------------------------------
44//	Includes
45//-----------------------------------------------------------------------------
46
47#include "AppleSCSIPDT00Emulator.h"
48
49#include <IOKit/IOMemoryDescriptor.h>
50
51#include <IOKit/scsi/SCSICommandOperationCodes.h>
52#include <IOKit/scsi/SCSICmds_INQUIRY_Definitions.h>
53#include <IOKit/scsi/SCSICmds_REPORT_LUNS_Definitions.h>
54#include <IOKit/scsi/SCSICmds_READ_CAPACITY_Definitions.h>
55
56
57//-----------------------------------------------------------------------------
58//	Macros
59//-----------------------------------------------------------------------------
60
61#define DEBUG 												1
62#define DEBUG_ASSERT_COMPONENT_NAME_STRING					"PDT00Emulator"
63
64#if DEBUG
65#define EMULATOR_ADAPTER_DEBUGGING_LEVEL					4
66#endif
67
68#include "DebugSupport.h"
69
70#if ( EMULATOR_ADAPTER_DEBUGGING_LEVEL >= 1 )
71#define PANIC_NOW(x)		panic x
72#else
73#define PANIC_NOW(x)
74#endif
75
76#if ( EMULATOR_ADAPTER_DEBUGGING_LEVEL >= 2 )
77#define ERROR_LOG(x)		IOLog x; IOSleep(1)
78#else
79#define ERROR_LOG(x)
80#endif
81
82#if ( EMULATOR_ADAPTER_DEBUGGING_LEVEL >= 3 )
83#define STATUS_LOG(x)		IOLog x; IOSleep(1)
84#else
85#define STATUS_LOG(x)
86#endif
87
88#if ( EMULATOR_ADAPTER_DEBUGGING_LEVEL >= 4 )
89#define COMMAND_LOG(x)		IOLog x; IOSleep(1)
90#else
91#define COMMAND_LOG(x)
92#endif
93
94
95#define super AppleSCSILogicalUnitEmulator
96OSDefineMetaClassAndStructors ( AppleSCSIPDT00Emulator, AppleSCSILogicalUnitEmulator );
97
98
99//-----------------------------------------------------------------------------
100//	WithCapacity
101//-----------------------------------------------------------------------------
102
103AppleSCSIPDT00Emulator *
104AppleSCSIPDT00Emulator::WithCapacity ( UInt64 capacity )
105{
106
107	AppleSCSIPDT00Emulator *	logicalUnit = NULL;
108	bool						result		= false;
109
110	STATUS_LOG ( ( "AppleSCSIPDT00Emulator::WithCapacity, capacity = %qd\n", capacity ) );
111
112	logicalUnit = OSTypeAlloc ( AppleSCSIPDT00Emulator );
113	require_nonzero ( logicalUnit, ErrorExit );
114
115	result = logicalUnit->InitWithCapacity ( capacity );
116	require ( result, ReleaseLogicalUnit );
117
118	return logicalUnit;
119
120
121ReleaseLogicalUnit:
122
123
124	logicalUnit->release ( );
125
126
127ErrorExit:
128
129
130	return NULL;
131
132}
133
134
135//-----------------------------------------------------------------------------
136//	InitWithCapacity
137//-----------------------------------------------------------------------------
138
139bool
140AppleSCSIPDT00Emulator::InitWithCapacity ( UInt64 capacity )
141{
142
143	fMemoryBuffer = IOBufferMemoryDescriptor::inTaskWithOptions (
144		kernel_task,
145		0,
146		capacity,
147		PAGE_SIZE );
148
149	require_nonzero ( fMemoryBuffer, ErrorExit );
150
151	fMemory 	= ( UInt8 * ) fMemoryBuffer->getBytesNoCopy ( );
152	fBufferSize = capacity;
153
154	return true;
155
156
157ErrorExit:
158
159
160	return false;
161
162}
163
164
165//-----------------------------------------------------------------------------
166//	SetDeviceBuffers
167//-----------------------------------------------------------------------------
168
169bool
170AppleSCSIPDT00Emulator::SetDeviceBuffers (
171		IOMemoryDescriptor * 	inquiryBuffer,
172		IOMemoryDescriptor * 	inquiryPage00Buffer,
173		IOMemoryDescriptor * 	inquiryPage80Buffer,
174		IOMemoryDescriptor * 	inquiryPage83Buffer )
175{
176
177	require_nonzero ( inquiryBuffer, ErrorExit );
178	require_nonzero ( inquiryPage00Buffer, ErrorExit );
179	require_nonzero ( inquiryPage80Buffer, ErrorExit );
180	require_nonzero ( inquiryPage83Buffer, ErrorExit );
181
182	fInquiryDataSize 		= inquiryBuffer->getLength ( );
183	fInquiryPage00DataSize	= inquiryPage00Buffer->getLength ( );
184	fInquiryPage80DataSize	= inquiryPage80Buffer->getLength ( );
185	fInquiryPage83DataSize	= inquiryPage83Buffer->getLength ( );
186
187	ERROR_LOG ( ( "fInquiryDataSize = %d\n", fInquiryDataSize ) );
188	ERROR_LOG ( ( "fInquiryPage00DataSize = %d\n", fInquiryPage00DataSize ) );
189	ERROR_LOG ( ( "fInquiryPage80DataSize = %d\n", fInquiryPage80DataSize ) );
190	ERROR_LOG ( ( "fInquiryPage83DataSize = %d\n", fInquiryPage83DataSize ) );
191
192	fInquiryData = ( UInt8 * ) IOMalloc ( fInquiryDataSize );
193	require_nonzero ( fInquiryData, ErrorExit );
194
195	fInquiryPage00Data = ( UInt8 * ) IOMalloc ( fInquiryPage00DataSize );
196	require_nonzero ( fInquiryPage00Data, ErrorExit );
197
198	fInquiryPage80Data = ( UInt8 * ) IOMalloc ( fInquiryPage80DataSize );
199	require_nonzero ( fInquiryPage80Data, ErrorExit );
200
201	fInquiryPage83Data = ( UInt8 * ) IOMalloc ( fInquiryPage83DataSize );
202	require_nonzero ( fInquiryPage83Data, ErrorExit );
203
204	inquiryBuffer->readBytes ( 0, fInquiryData, fInquiryDataSize );
205	inquiryPage00Buffer->readBytes ( 0, fInquiryPage00Data, fInquiryPage00DataSize );
206	inquiryPage80Buffer->readBytes ( 0, fInquiryPage80Data, fInquiryPage80DataSize );
207	inquiryPage83Buffer->readBytes ( 0, fInquiryPage83Data, fInquiryPage83DataSize );
208
209	return true;
210
211
212ErrorExit:
213
214
215	return false;
216
217}
218
219
220//-----------------------------------------------------------------------------
221//	free
222//-----------------------------------------------------------------------------
223
224void
225AppleSCSIPDT00Emulator::free ( void )
226{
227
228	STATUS_LOG ( ( "AppleSCSIPDT00Emulator::free\n" ) );
229
230	if ( fMemoryBuffer != NULL )
231	{
232
233		fMemoryBuffer->release ( );
234		fMemoryBuffer = NULL;
235
236	}
237
238	if ( fInquiryData != NULL )
239	{
240
241		IOFree ( fInquiryData, fInquiryDataSize );
242		fInquiryData = NULL;
243
244	}
245
246	if ( fInquiryPage00Data != NULL )
247	{
248
249		IOFree ( fInquiryPage00Data, fInquiryPage00DataSize );
250		fInquiryPage00Data = NULL;
251
252	}
253
254	if ( fInquiryPage80Data != NULL )
255	{
256
257		IOFree ( fInquiryPage80Data, fInquiryPage80DataSize );
258		fInquiryPage80Data = NULL;
259
260	}
261
262	if ( fInquiryPage83Data != NULL )
263	{
264
265		IOFree ( fInquiryPage83Data, fInquiryPage83DataSize );
266		fInquiryPage83Data = NULL;
267
268	}
269
270	super::free ( );
271
272}
273
274
275//-----------------------------------------------------------------------------
276//	SendCommand
277//-----------------------------------------------------------------------------
278
279int
280AppleSCSIPDT00Emulator::SendCommand (
281	UInt8 *					cdb,
282	UInt8 					cbdLen,
283	IOMemoryDescriptor *	dataDesc,
284	UInt64 *				dataLen,
285	SCSITaskStatus *		scsiStatus,
286	SCSI_Sense_Data *		senseBuffer,
287	UInt8 *					senseBufferLen )
288{
289
290	UInt32		lba;
291	UInt16 		transferLength;
292	UInt32		byteOffset;
293	UInt32		numBytes;
294
295	STATUS_LOG ( ( "AppleSCSIPDT00Emulator::sendCommand, LUN = %qd\n", GetLogicalUnitNumber ( ) ) );
296
297	switch ( cdb[0] )
298	{
299
300		case kSCSICmd_TEST_UNIT_READY:
301		{
302
303			COMMAND_LOG ( ( "SCSI Command: TEST_UNIT_READY\n" ) );
304
305			*scsiStatus = kSCSITaskStatus_GOOD;
306			*dataLen = 0;
307			break;
308
309		}
310
311		case kSCSICmd_INQUIRY:
312		{
313
314			COMMAND_LOG ( ( "SCSI Command: INQUIRY\n" ) );
315
316			if ( cdb[1] == 1 )
317			{
318
319				UInt8 *		buffer;
320				UInt64		amount;
321
322				COMMAND_LOG ( ( "INQUIRY VPD requested\n" ) );
323
324				// Asking for EVPD. Return EVPD data based on PAGE_CODE parameter.
325				if ( cdb[2] == kINQUIRY_Page00_PageCode )
326				{
327
328					COMMAND_LOG ( ( "Page 00h requested\n" ) );
329
330					buffer = ( UInt8 * ) fInquiryPage00Data;
331					amount = fInquiryPage00DataSize;
332
333				}
334
335				else if ( cdb[2] == kINQUIRY_Page80_PageCode )
336				{
337
338					COMMAND_LOG ( ( "Page 80h requested\n" ) );
339
340					buffer = ( UInt8 * ) fInquiryPage80Data;
341					amount = fInquiryPage80DataSize;
342
343				}
344
345				else if ( cdb[2] == kINQUIRY_Page83_PageCode )
346				{
347
348					COMMAND_LOG ( ( "Page 83h requested\n" ) );
349
350					buffer = ( UInt8 * ) fInquiryPage83Data;
351					amount = fInquiryPage83DataSize;
352
353				}
354
355				if ( buffer != NULL )
356				{
357
358					COMMAND_LOG ( ( "Requested = %llu\n", *dataLen ) );
359					COMMAND_LOG ( ( "Amount = %llu\n", amount ) );
360
361					*dataLen = min ( amount, *dataLen );
362					dataDesc->writeBytes ( 0, buffer, *dataLen );
363
364					COMMAND_LOG ( ( "Realized = %llu\n", *dataLen ) );
365
366					*scsiStatus = kSCSITaskStatus_GOOD;
367
368				}
369
370				else
371				{
372
373					*scsiStatus = kSCSITaskStatus_CHECK_CONDITION;
374
375					if ( senseBuffer != NULL )
376					{
377
378						UInt8	amount = min ( *senseBufferLen, sizeof ( SCSI_Sense_Data ) );
379
380						bzero ( senseBuffer, *senseBufferLen );
381						bcopy ( &gInvalidCDBFieldSenseData, senseBuffer, amount );
382
383						*senseBufferLen = amount;
384
385					}
386
387				}
388
389			}
390
391			else if ( ( cdb[1] == 2 ) || ( cdb[2] != 0 ) || ( cdb[3] != 0 ) )
392			{
393
394				COMMAND_LOG ( ( "Illegal request\n" ) );
395
396				// Don't support CMDDT bit, or PAGE_CODE without EVPD set.
397				*scsiStatus = kSCSITaskStatus_CHECK_CONDITION;
398
399				if ( senseBuffer != NULL )
400				{
401
402					UInt8	amount = min ( *senseBufferLen, sizeof ( SCSI_Sense_Data ) );
403
404					bzero ( senseBuffer, *senseBufferLen );
405					bcopy ( &gInvalidCDBFieldSenseData, senseBuffer, amount );
406
407					*senseBufferLen = amount;
408
409				}
410
411			}
412
413			else
414			{
415
416				COMMAND_LOG ( ( "Standard INQUIRY\n" ) );
417
418				*dataLen = min ( fInquiryDataSize, *dataLen );
419				dataDesc->writeBytes ( 0, fInquiryData, *dataLen );
420
421				*scsiStatus = kSCSITaskStatus_GOOD;
422
423			}
424
425		}
426		break;
427
428		case kSCSICmd_READ_CAPACITY:
429		{
430
431			SCSI_Capacity_Data	data;
432			UInt32				lastBlock;
433
434			COMMAND_LOG ( ( "SCSI Command: READ_CAPACITY\n" ) );
435
436			lastBlock = ( fBufferSize / kBlockSize ) - 1;
437			data.RETURNED_LOGICAL_BLOCK_ADDRESS = OSSwapHostToBigInt32 ( lastBlock );
438			data.BLOCK_LENGTH_IN_BYTES = OSSwapHostToBigInt32 ( kBlockSize );
439
440			*dataLen = min ( sizeof ( data ), *dataLen );
441
442			dataDesc->writeBytes ( 0, &data, *dataLen );
443
444			*scsiStatus = kSCSITaskStatus_GOOD;
445
446		}
447		break;
448
449		case kSCSICmd_WRITE_10:
450		{
451
452			lba				= OSReadBigInt32 ( cdb, 2 );
453			transferLength 	= OSReadBigInt16 ( cdb, 7 );
454
455			byteOffset 		= lba * kBlockSize;
456			numBytes 		= transferLength * kBlockSize;
457
458			COMMAND_LOG ( ( "SCSI Command: WRITE_10 - %d (0x%X) bytes at 0x%X (ptr = %p)\n", numBytes, numBytes, byteOffset, &fMemory[byteOffset] ) );
459
460			dataDesc->readBytes ( 0, &fMemory[byteOffset], numBytes );
461
462			*scsiStatus = kSCSITaskStatus_GOOD;
463
464		}
465		break;
466
467		case kSCSICmd_READ_10:
468		{
469
470			lba				= OSReadBigInt32 ( cdb, 2 );
471			transferLength 	= OSReadBigInt16 ( cdb, 7 );
472
473			byteOffset		= lba * kBlockSize;
474			numBytes		= transferLength * kBlockSize;
475
476			COMMAND_LOG ( ( "SCSI Command: READ_10 - %qd (0x%qX) bytes at 0x%X (ptr = %p)\n", *dataLen, *dataLen, byteOffset, &fMemory[byteOffset] ) );
477
478			dataDesc->writeBytes ( 0, &fMemory[byteOffset], *dataLen );
479
480			*scsiStatus = kSCSITaskStatus_GOOD;
481
482		}
483		break;
484
485		case kSCSICmd_START_STOP_UNIT:
486		{
487
488			COMMAND_LOG ( ( "SCSI Command: START_STOP_UNIT\n" ) );
489
490			// For now just set the status to success.
491			*scsiStatus = kSCSITaskStatus_GOOD;
492
493		}
494		break;
495
496		case kSCSICmd_PREVENT_ALLOW_MEDIUM_REMOVAL:
497		{
498
499			COMMAND_LOG ( ( "SCSI Command: PREVENT_ALLOW_MEDIUM_REMOVAL - prevent = 0x%02X\n", cdb[4] & 0x03 ) );
500
501			// We're not a changeable medium... safe to ignore for now
502			*scsiStatus = kSCSITaskStatus_GOOD;
503			*senseBufferLen = 0;
504
505		}
506		break;
507
508		case kSCSICmd_REQUEST_SENSE:
509		{
510
511			COMMAND_LOG ( ( "SCSI Command: REQUEST_SENSE (desc = %s, allocation length = %d bytes) - returning CHECK CONDITION with INVALID COMMAND\n", (cdb[1] & 0x01) ? "TRUE" : "FALSE", cdb[4] ) );
512
513			*scsiStatus = kSCSITaskStatus_CHECK_CONDITION;
514			*dataLen = 0;
515
516			if ( senseBuffer != NULL )
517			{
518
519				UInt8	amount = min ( *senseBufferLen, sizeof ( SCSI_Sense_Data ) );
520
521				bzero ( senseBuffer, *senseBufferLen );
522				bcopy ( &gInvalidCommandSenseData, senseBuffer, amount );
523
524				*senseBufferLen = amount;
525
526			}
527
528		}
529		break;
530
531
532		case kSCSICmd_MODE_SENSE_6:
533		{
534
535			SPCModeParameterHeader6		header;
536
537			COMMAND_LOG ( ( "SCSI Command: MODE_SENSE_6\n" ) );
538
539			// We don't support any mode pages, but we support the mode parameter header.
540			// Just return the header.
541			*senseBufferLen = 0;
542
543			COMMAND_LOG ( ( "*dataLen = %llu, sizeof(header) = %lu\n", *dataLen, sizeof ( header ) ) );
544			COMMAND_LOG ( ( "pageCode = 0x%02x\n", cdb[2] & 0x3FFFF ) );
545
546			*dataLen = min ( sizeof ( header ), *dataLen );
547
548			header.MODE_DATA_LENGTH				= sizeof ( header ) - sizeof ( header.MODE_DATA_LENGTH );
549			header.MEDIUM_TYPE					= 0;	// Must be 0h by SBC spec.
550			header.DEVICE_SPECIFIC_PARAMETER	= 0;	// Not write protected. Doesn't support DPOFUA.
551			header.BLOCK_DESCRIPTOR_LENGTH		= 0;	// No block descriptors.
552
553			COMMAND_LOG ( ( "header.MODE_DATA_LENGTH = %d, *dataLen = %llu\n", header.MODE_DATA_LENGTH, *dataLen ) );
554
555			dataDesc->writeBytes ( 0, &header, *dataLen );
556
557			*scsiStatus = kSCSITaskStatus_GOOD;
558
559		}
560		break;
561
562		case kSCSICmd_MODE_SENSE_10:
563		{
564
565			SPCModeParameterHeader10		header;
566
567			COMMAND_LOG ( ( "SCSI Command: MODE_SENSE_10\n" ) );
568
569			// We don't support any mode pages, but we support the mode parameter header.
570			// Just return the header.
571			*senseBufferLen = 0;
572
573			COMMAND_LOG ( ( "*dataLen = %llu, sizeof(header) = %lu\n", *dataLen, sizeof ( header ) ) );
574			COMMAND_LOG ( ( "pageCode = 0x%02x\n", cdb[2] & 0x3FFFF ) );
575
576			*dataLen = min ( sizeof ( header ), *dataLen );
577
578			header.MODE_DATA_LENGTH				= sizeof ( header ) - sizeof ( header.MODE_DATA_LENGTH );
579			header.MEDIUM_TYPE					= 0;	// Must be 0h by SBC spec.
580			header.DEVICE_SPECIFIC_PARAMETER	= 0;	// Not write protected. Doesn't support DPOFUA.
581			header.BLOCK_DESCRIPTOR_LENGTH		= 0;	// No block descriptors.
582
583			COMMAND_LOG ( ( "header.MODE_DATA_LENGTH = %d, *dataLen = %llu\n", header.MODE_DATA_LENGTH, *dataLen ) );
584
585			dataDesc->writeBytes ( 0, &header, *dataLen );
586
587			*scsiStatus = kSCSITaskStatus_GOOD;
588
589		}
590		break;
591
592		default:
593		{
594
595			COMMAND_LOG ( ( "SCSI Command: Unknown: 0x%X\n", cdb[0] ) );
596
597			*scsiStatus = kSCSITaskStatus_CHECK_CONDITION;
598
599			if ( senseBuffer != NULL )
600			{
601
602				UInt8	amount = min ( *senseBufferLen, sizeof ( SCSI_Sense_Data ) );
603
604				bzero ( senseBuffer, *senseBufferLen );
605				bcopy ( &gInvalidCommandSenseData, senseBuffer, amount );
606
607				*senseBufferLen = amount;
608
609			}
610
611		}
612		break;
613
614	}
615
616	return 1;
617
618}