1/*
2 * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//-----------------------------------------------------------------------------
26//	Includes
27//-----------------------------------------------------------------------------
28
29// SCSI Parallel Family includes
30#include "SCSIParallelTask.h"
31
32// General IOKit includes
33#include <IOKit/IOLib.h>
34#include <IOKit/IOBufferMemoryDescriptor.h>
35#include <IOKit/IODMACommand.h>
36
37
38//-----------------------------------------------------------------------------
39//	Macros
40//-----------------------------------------------------------------------------
41
42#define DEBUG 												0
43#define DEBUG_ASSERT_COMPONENT_NAME_STRING					"SCSIParallelTask"
44
45#if DEBUG
46#define SCSI_PARALLEL_TASK_DEBUGGING_LEVEL					0
47#endif
48
49
50// This module needs SPI_MODULE defined in order to pick up the
51// static debugging function.
52#define SPI_MODULE	1
53
54#include "IOSCSIParallelFamilyDebugging.h"
55
56
57#if ( SCSI_PARALLEL_TASK_DEBUGGING_LEVEL >= 1 )
58#define PANIC_NOW(x)		panic x
59#else
60#define PANIC_NOW(x)
61#endif
62
63#if ( SCSI_PARALLEL_TASK_DEBUGGING_LEVEL >= 2 )
64#define ERROR_LOG(x)		kprintf x
65#else
66#define ERROR_LOG(x)
67#endif
68
69#if ( SCSI_PARALLEL_TASK_DEBUGGING_LEVEL >= 3 )
70#define STATUS_LOG(x)		kprintf x
71#else
72#define STATUS_LOG(x)
73#endif
74
75
76#define super IODMACommand
77OSDefineMetaClassAndStructors ( SCSIParallelTask, IODMACommand );
78
79
80#if 0
81#pragma mark -
82#pragma mark Public Methods
83#pragma mark -
84#endif
85
86
87//-----------------------------------------------------------------------------
88//	Create - Creates a SCSIParallelTask						   [STATIC][PUBLIC]
89//-----------------------------------------------------------------------------
90
91SCSIParallelTask *
92SCSIParallelTask::Create ( UInt32 sizeOfHBAData, UInt64 alignmentMask )
93{
94
95	SCSIParallelTask *	newTask = NULL;
96	bool				result	= false;
97
98	newTask = OSTypeAlloc ( SCSIParallelTask );
99	require_nonzero ( newTask, ErrorExit );
100
101	result = newTask->InitWithSize ( sizeOfHBAData, alignmentMask );
102	require ( result, ReleaseTask );
103
104	return newTask;
105
106
107ReleaseTask:
108
109
110	newTask->release ( );
111	newTask = NULL;
112
113
114ErrorExit:
115
116
117	return NULL;
118
119}
120
121
122//-----------------------------------------------------------------------------
123//	InitWithSize - Initializes the object with requested HBA data size.
124//																	   [PUBLIC]
125//-----------------------------------------------------------------------------
126
127bool
128SCSIParallelTask::InitWithSize (
129	UInt32		sizeOfHBAData,
130	UInt64 		alignmentMask )
131{
132
133	IOBufferMemoryDescriptor *	buffer			= NULL;
134	IOReturn					status			= kIOReturnSuccess;
135
136	fCommandChain.next = NULL;
137	fCommandChain.prev = NULL;
138
139	fResendTaskChain.next = NULL;
140	fResendTaskChain.prev = NULL;
141
142	fTimeoutChain.next 	= NULL;
143	fTimeoutChain.prev	= NULL;
144
145	fHBADataSize = sizeOfHBAData;
146
147	buffer = IOBufferMemoryDescriptor::inTaskWithPhysicalMask (
148		kernel_task,
149		kIODirectionOutIn | kIOMemoryPhysicallyContiguous,
150		fHBADataSize,
151		alignmentMask );
152	require_nonzero ( buffer, ErrorExit );
153
154	status = buffer->prepare ( kIODirectionOutIn );
155	require_success ( status, FreeHBAData );
156
157	fHBAData = buffer->getBytesNoCopy ( );
158	require_nonzero ( fHBAData, CompleteHBAData );
159
160	bzero ( fHBAData, fHBADataSize );
161
162	fHBADataDescriptor = buffer;
163
164	return true;
165
166
167CompleteHBAData:
168
169
170	buffer->complete ( );
171
172
173FreeHBAData:
174
175
176	buffer->release ( );
177	buffer = NULL;
178
179
180ErrorExit:
181
182
183	return false;
184
185}
186
187
188//-----------------------------------------------------------------------------
189//	free - Frees any resources allocated.							   [PUBLIC]
190//-----------------------------------------------------------------------------
191
192void
193SCSIParallelTask::free ( void )
194{
195
196	if ( fHBADataDescriptor != NULL )
197	{
198
199		fHBADataDescriptor->complete ( );
200		fHBADataDescriptor->release ( );
201		fHBADataDescriptor = NULL;
202
203	}
204
205	super::free ( );
206
207}
208
209
210//-----------------------------------------------------------------------------
211//	ResetForNewTask - Resets the task for execution.				   [PUBLIC]
212//-----------------------------------------------------------------------------
213
214void
215SCSIParallelTask::ResetForNewTask ( void )
216{
217
218	fTargetID					= 0;
219	fDevice						= NULL;
220	fSCSITask					= NULL;
221	fRealizedTransferCount		= 0;
222	fControllerTaskIdentifier	= 0;
223	fTaskRetryCount				= 0;
224
225	fSCSIParallelFeatureRequestCount		= 0;
226	fSCSIParallelFeatureRequestResultCount	= 0;
227
228	// Set the feature arrays to their default values
229	for ( int loop = 0; loop < kSCSIParallelFeature_TotalFeatureCount; loop++ )
230	{
231
232		fSCSIParallelFeatureRequest[loop]	= kSCSIParallelFeature_NoNegotiation;
233		fSCSIParallelFeatureResult[loop]	= kSCSIParallelFeature_NegotitiationUnchanged;
234
235	}
236
237}
238
239
240//-----------------------------------------------------------------------------
241//	SetSCSITaskIdentifier - Sets SCSITaskIdentifier for this task.	   [PUBLIC]
242//-----------------------------------------------------------------------------
243
244bool
245SCSIParallelTask::SetSCSITaskIdentifier ( SCSITaskIdentifier scsiRequest )
246{
247
248	fSCSITask = scsiRequest;
249	return true;
250
251}
252
253
254//-----------------------------------------------------------------------------
255//	GetSCSITaskIdentifier - Gets SCSITaskIdentifier for this task.	   [PUBLIC]
256//-----------------------------------------------------------------------------
257
258SCSITaskIdentifier
259SCSIParallelTask::GetSCSITaskIdentifier ( void )
260{
261	return fSCSITask;
262}
263
264
265//-----------------------------------------------------------------------------
266//	SetTargetIdentifier - Sets SCSITargetIdentifier for this task.	   [PUBLIC]
267//-----------------------------------------------------------------------------
268
269bool
270SCSIParallelTask::SetTargetIdentifier ( SCSITargetIdentifier theTargetID )
271{
272	fTargetID = theTargetID;
273	return true;
274}
275
276
277//-----------------------------------------------------------------------------
278//	GetTargetIdentifier - Gets SCSITargetIdentifier for this task.	   [PUBLIC]
279//-----------------------------------------------------------------------------
280
281SCSITargetIdentifier
282SCSIParallelTask::GetTargetIdentifier ( void )
283{
284	return fTargetID;
285}
286
287
288//-----------------------------------------------------------------------------
289//	SetDevice - Sets device for this task.							   [PUBLIC]
290//-----------------------------------------------------------------------------
291
292bool
293SCSIParallelTask::SetDevice ( IOSCSIParallelInterfaceDevice * device )
294{
295	fDevice = device;
296	return true;
297}
298
299
300//-----------------------------------------------------------------------------
301//	GetDevice - Gets device for this task.							   [PUBLIC]
302//-----------------------------------------------------------------------------
303
304IOSCSIParallelInterfaceDevice *
305SCSIParallelTask::GetDevice ( void )
306{
307	return fDevice;
308}
309
310
311//-----------------------------------------------------------------------------
312//	GetLogicalUnitNumber - Gets SCSILogicalUnitNumber for this task.   [PUBLIC]
313//-----------------------------------------------------------------------------
314
315SCSILogicalUnitNumber
316SCSIParallelTask::GetLogicalUnitNumber ( void )
317{
318	return ( ( SCSITask * ) fSCSITask )->GetLogicalUnitNumber ( );
319}
320
321//-----------------------------------------------------------------------------
322//	GetLogicalUnitBytes - Gets SCSILogicalUnitBytes for this task.   [PUBLIC]
323//-----------------------------------------------------------------------------
324
325void
326SCSIParallelTask::GetLogicalUnitBytes ( SCSILogicalUnitBytes * logicalUnitBytes )
327{
328	return ( ( SCSITask * ) fSCSITask )->GetLogicalUnitBytes ( logicalUnitBytes );
329}
330
331//-----------------------------------------------------------------------------
332//	GetTaskAttribute - Gets SCSITaskAttribute for this task. 		   [PUBLIC]
333//-----------------------------------------------------------------------------
334
335SCSITaskAttribute
336SCSIParallelTask::GetTaskAttribute ( void )
337{
338	return ( ( SCSITask * ) fSCSITask )->GetTaskAttribute ( );
339}
340
341
342//-----------------------------------------------------------------------------
343//	GetTaggedTaskIdentifier - Gets SCSITaggedTaskIdentifier for this task.
344//															 		   [PUBLIC]
345//-----------------------------------------------------------------------------
346
347SCSITaggedTaskIdentifier
348SCSIParallelTask::GetTaggedTaskIdentifier( void )
349{
350	return ( ( SCSITask * ) fSCSITask )->GetTaggedTaskIdentifier ( );
351}
352
353
354//-----------------------------------------------------------------------------
355//	GetCommandDescriptorBlockSize - Gets cdb size for this task. 	   [PUBLIC]
356//-----------------------------------------------------------------------------
357
358UInt8
359SCSIParallelTask::GetCommandDescriptorBlockSize ( void )
360{
361	return ( ( SCSITask * ) fSCSITask )->GetCommandDescriptorBlockSize ( );
362}
363
364
365//-----------------------------------------------------------------------------
366//	GetCommandDescriptorBlock - Gets cdb for this task. 			   [PUBLIC]
367//-----------------------------------------------------------------------------
368
369bool
370SCSIParallelTask::GetCommandDescriptorBlock (
371					SCSICommandDescriptorBlock *	cdbData )
372{
373	return ( ( SCSITask * ) fSCSITask )->GetCommandDescriptorBlock ( cdbData );
374}
375
376
377//-----------------------------------------------------------------------------
378//	GetDataTransferDirection - Gets transfer direction for this task.  [PUBLIC]
379//-----------------------------------------------------------------------------
380
381UInt8
382SCSIParallelTask::GetDataTransferDirection ( void )
383{
384	return ( ( SCSITask * ) fSCSITask )->GetDataTransferDirection ( );
385}
386
387
388//-----------------------------------------------------------------------------
389//	GetRequestedDataTransferCount - Gets transfer count for this task. [PUBLIC]
390//-----------------------------------------------------------------------------
391
392UInt64
393SCSIParallelTask::GetRequestedDataTransferCount ( void )
394{
395	return ( ( SCSITask * ) fSCSITask )->GetRequestedDataTransferCount ( );
396}
397
398
399//-----------------------------------------------------------------------------
400//	IncrementRealizedDataTransferCount - Adds value to the realized transfer
401//										 count for this task. 		   [PUBLIC]
402//-----------------------------------------------------------------------------
403
404void
405SCSIParallelTask::IncrementRealizedDataTransferCount (
406					UInt64 							realizedTransferCountInBytes )
407{
408	fRealizedTransferCount += realizedTransferCountInBytes;
409}
410
411
412//-----------------------------------------------------------------------------
413//	SetRealizedDataTransferCount - 	Sets the realized transfer count for
414//									this task. 		 				   [PUBLIC]
415//-----------------------------------------------------------------------------
416
417bool
418SCSIParallelTask::SetRealizedDataTransferCount (
419					UInt64 							realizedTransferCountInBytes )
420{
421
422	fRealizedTransferCount = realizedTransferCountInBytes;
423	return true;
424
425}
426
427
428//-----------------------------------------------------------------------------
429//	GetRealizedDataTransferCount - 	Gets the realized transfer count for
430//									this task. 		 				   [PUBLIC]
431//-----------------------------------------------------------------------------
432
433UInt64
434SCSIParallelTask::GetRealizedDataTransferCount ( void )
435{
436	return fRealizedTransferCount;
437}
438
439
440//-----------------------------------------------------------------------------
441//	GetDataBuffer - Gets the data buffer associated with this task.    [PUBLIC]
442//-----------------------------------------------------------------------------
443
444IOMemoryDescriptor *
445SCSIParallelTask::GetDataBuffer ( void )
446{
447	return ( ( SCSITask * ) fSCSITask )->GetDataBuffer ( );
448}
449
450
451//-----------------------------------------------------------------------------
452//	GetDataBufferOffset - 	Gets the data buffer offset associated with this
453//							task.									   [PUBLIC]
454//-----------------------------------------------------------------------------
455
456UInt64
457SCSIParallelTask::GetDataBufferOffset ( void )
458{
459	return ( ( SCSITask * ) fSCSITask )->GetDataBufferOffset ( );
460}
461
462
463//-----------------------------------------------------------------------------
464//	GetTimeoutDuration - 	Gets the timeout duration associated with this
465//							task.									   [PUBLIC]
466//-----------------------------------------------------------------------------
467
468UInt32
469SCSIParallelTask::GetTimeoutDuration ( void )
470{
471	return ( ( SCSITask * ) fSCSITask )->GetTimeoutDuration ( );
472}
473
474
475//-----------------------------------------------------------------------------
476//	SetAutoSenseData - 	Sets the auto-sense data.					   [PUBLIC]
477//-----------------------------------------------------------------------------
478
479bool
480SCSIParallelTask::SetAutoSenseData (
481					SCSI_Sense_Data * 	senseData,
482					UInt8				senseDataSize )
483{
484	return ( ( SCSITask * ) fSCSITask )->SetAutoSenseData ( senseData, senseDataSize );
485}
486
487
488//-----------------------------------------------------------------------------
489//	GetAutoSenseData - 	Gets the auto-sense data.					   [PUBLIC]
490//-----------------------------------------------------------------------------
491
492bool
493SCSIParallelTask::GetAutoSenseData (
494					SCSI_Sense_Data * 	receivingBuffer,
495					UInt8				senseDataSize )
496{
497	return ( ( SCSITask * ) fSCSITask )->GetAutoSenseData ( receivingBuffer, senseDataSize );
498}
499
500
501//-----------------------------------------------------------------------------
502//	GetAutoSenseDataSize - 	Gets the auto-sense data size.			   [PUBLIC]
503//-----------------------------------------------------------------------------
504
505UInt8
506SCSIParallelTask::GetAutoSenseDataSize ( void )
507{
508	return ( ( SCSITask * ) fSCSITask )->GetAutoSenseDataSize ( );
509}
510
511
512//-----------------------------------------------------------------------------
513//	SetSCSIParallelFeatureNegotiation - Sets a feature negotiation request.
514//																	   [PUBLIC]
515//-----------------------------------------------------------------------------
516
517void
518SCSIParallelTask::SetSCSIParallelFeatureNegotiation (
519					SCSIParallelFeature 			requestedFeature,
520					SCSIParallelFeatureRequest 		newRequest )
521{
522
523	// Check to see if this is a known feature.  Since the feature definitions
524	// are zero based if the request is greater or equal than the total number
525	// of features it is undefined.
526	if ( requestedFeature >= kSCSIParallelFeature_TotalFeatureCount )
527	{
528
529		ERROR_LOG ( ( "Unknown feature request: %ld\n", ( int ) requestedFeature ) );
530
531		// The object does not know of this feature, so it will
532		// ignore this request.
533		return;
534
535	}
536
537	// If this request is either to negotiate or clear a negotiation,
538	// increment the requested feature count.
539	if ( newRequest != kSCSIParallelFeature_NoNegotiation )
540	{
541		fSCSIParallelFeatureRequestCount++;
542	}
543
544	fSCSIParallelFeatureRequest[requestedFeature] = newRequest;
545
546}
547
548
549//-----------------------------------------------------------------------------
550//	GetSCSIParallelFeatureNegotiation - Gets a feature negotiation request.
551//																	   [PUBLIC]
552//-----------------------------------------------------------------------------
553
554SCSIParallelFeatureRequest
555SCSIParallelTask::GetSCSIParallelFeatureNegotiation (
556					SCSIParallelFeature 			requestedFeature )
557{
558
559	// Check to see if this is a known feature.  Since the feature definitions
560	// are zero based if the request is greater or equal than the total number
561	// of features it is undefined.
562	if ( requestedFeature >= kSCSIParallelFeature_TotalFeatureCount )
563	{
564
565		ERROR_LOG ( ( "Unknown feature request: %ld\n", ( int ) requestedFeature ) );
566
567		// The object does not know of this feature, so it will
568		// return that negotation is not requested.
569		return kSCSIParallelFeature_NoNegotiation;
570
571	}
572
573	return fSCSIParallelFeatureRequest[requestedFeature];
574
575}
576
577
578//-----------------------------------------------------------------------------
579//	GetSCSIParallelFeatureNegotiationCount - Gets feature negotiation request count.
580//																	   [PUBLIC]
581//-----------------------------------------------------------------------------
582
583UInt64
584SCSIParallelTask::GetSCSIParallelFeatureNegotiationCount ( void )
585{
586	return fSCSIParallelFeatureRequestCount;
587}
588
589
590//-----------------------------------------------------------------------------
591//	SetSCSIParallelFeatureNegotiationResult - Sets feature negotiation result.
592//																	   [PUBLIC]
593//-----------------------------------------------------------------------------
594
595void
596SCSIParallelTask::SetSCSIParallelFeatureNegotiationResult (
597					SCSIParallelFeature 			requestedFeature,
598					SCSIParallelFeatureResult 		newResult )
599{
600
601	// Check to see if this is a known feature.  Since the feature definitions
602	// are zero based if the request is greater or equal than the total number
603	// of features it is undefined.
604	if ( requestedFeature >= kSCSIParallelFeature_TotalFeatureCount )
605	{
606
607		ERROR_LOG ( ( "Unknown feature request: %ld\n", ( int ) requestedFeature ) );
608
609		// The object does not know of this feature, so it will
610		// ignore this request.
611		return;
612
613	}
614
615	if ( newResult != kSCSIParallelFeature_NegotitiationUnchanged )
616	{
617		fSCSIParallelFeatureRequestResultCount++;
618	}
619
620	fSCSIParallelFeatureResult[requestedFeature] = newResult;
621
622}
623
624
625//-----------------------------------------------------------------------------
626//	GetSCSIParallelFeatureNegotiationResult - Gets feature negotiation result.
627//																	   [PUBLIC]
628//-----------------------------------------------------------------------------
629
630SCSIParallelFeatureResult
631SCSIParallelTask::GetSCSIParallelFeatureNegotiationResult (
632 					SCSIParallelFeature 			requestedFeature )
633{
634
635	// Check to see if this is a known feature.  Since the feature definitions
636	// are zero based if the request is greater or equal than the total number
637	// of features it is undefined.
638	if ( requestedFeature >= kSCSIParallelFeature_TotalFeatureCount )
639	{
640
641		ERROR_LOG ( ( "Unknown feature request: %ld\n", ( int ) requestedFeature ) );
642
643		// The object does not know of this feature, so it will
644		// return that negotation is unchanged.
645		return kSCSIParallelFeature_NegotitiationUnchanged;
646
647	}
648
649	return fSCSIParallelFeatureResult[requestedFeature];
650
651}
652
653
654//-----------------------------------------------------------------------------
655//	GetSCSIParallelFeatureNegotiationResultCount -  Gets feature negotiation
656//													result count.	   [PUBLIC]
657//-----------------------------------------------------------------------------
658
659UInt64
660SCSIParallelTask::GetSCSIParallelFeatureNegotiationResultCount ( void )
661{
662	return fSCSIParallelFeatureRequestResultCount;
663}
664
665
666//-----------------------------------------------------------------------------
667//	SetControllerTaskIdentifier -  Sets controller unique identifier.  [PUBLIC]
668//-----------------------------------------------------------------------------
669
670void
671SCSIParallelTask::SetControllerTaskIdentifier ( UInt64 newIdentifier )
672{
673	fControllerTaskIdentifier = newIdentifier;
674}
675
676
677//-----------------------------------------------------------------------------
678//	GetControllerTaskIdentifier -  Gets controller unique identifier.  [PUBLIC]
679//-----------------------------------------------------------------------------
680
681UInt64
682SCSIParallelTask::GetControllerTaskIdentifier ( void )
683{
684	return fControllerTaskIdentifier;
685}
686
687
688//-----------------------------------------------------------------------------
689//	GetHBADataSize -  Gets data size of HBA specific data.			   [PUBLIC]
690//-----------------------------------------------------------------------------
691
692UInt32
693SCSIParallelTask::GetHBADataSize ( void )
694{
695	return fHBADataSize;
696}
697
698
699//-----------------------------------------------------------------------------
700//	GetHBADataPointer -  Gets virtual address of HBA specific data.	   [PUBLIC]
701//-----------------------------------------------------------------------------
702
703void *
704SCSIParallelTask::GetHBADataPointer ( void )
705{
706	return fHBAData;
707}
708
709
710//-----------------------------------------------------------------------------
711//	GetHBADataDescriptor -  Gets IOMemoryDescriptor of HBA specific data.
712//																	   [PUBLIC]
713//-----------------------------------------------------------------------------
714
715IOMemoryDescriptor *
716SCSIParallelTask::GetHBADataDescriptor ( void )
717{
718	return fHBADataDescriptor;
719}
720
721
722//-----------------------------------------------------------------------------
723//	GetTimeoutDeadline -  Gets the timeout deadline in AbsoluteTime.   [PUBLIC]
724//-----------------------------------------------------------------------------
725
726AbsoluteTime
727SCSIParallelTask::GetTimeoutDeadline ( void )
728{
729	return fTimeoutDeadline;
730}
731
732
733//-----------------------------------------------------------------------------
734//	SetTimeoutDeadline -  Sets the timeout deadline in AbsoluteTime.   [PUBLIC]
735//-----------------------------------------------------------------------------
736
737void
738SCSIParallelTask::SetTimeoutDeadline ( AbsoluteTime time )
739{
740	fTimeoutDeadline = time;
741}
742
743
744#if 0
745#pragma mark -
746#pragma mark Static Debugging Assertion Method
747#pragma mark -
748#endif
749
750
751//-----------------------------------------------------------------------------
752//	IOSCSIParallelFamilyDebugAssert -  Assertion routine.
753//-----------------------------------------------------------------------------
754
755void
756IOSCSIParallelFamilyDebugAssert (	const char * componentNameString,
757									const char * assertionString,
758									const char * exceptionLabelString,
759									const char * errorString,
760									const char * fileName,
761									long		 lineNumber,
762									int 		 errorCode )
763{
764
765	kprintf ( "%s Assert failed: %s ", componentNameString, assertionString );
766
767	if ( exceptionLabelString != NULL )
768		kprintf ( "%s ", exceptionLabelString );
769
770	if ( errorString != NULL )
771		kprintf ( "%s ", errorString );
772
773	if ( fileName != NULL )
774		kprintf ( "file: %s ", fileName );
775
776	if ( lineNumber != 0 )
777		kprintf ( "line: %ld ", lineNumber );
778
779	if ( ( long ) errorCode != 0 )
780		kprintf ( "error: %ld ", ( long ) errorCode );
781
782	kprintf ( "\n" );
783
784}
785