]> git.saurik.com Git - apple/xnu.git/blob - iokit/Drivers/ata/drvAppleUltra66ATA/AppleUltra66ATA.cpp
xnu-201.42.3.tar.gz
[apple/xnu.git] / iokit / Drivers / ata / drvAppleUltra66ATA / AppleUltra66ATA.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 *
24 * AppleUltra66ATA.cpp
25 *
26 */
27 #include "AppleUltra66ATA.h"
28
29 #undef super
30 #define super IOATAStandardDriver
31
32 extern pmap_t kernel_pmap;
33
34 OSDefineMetaClassAndStructors( AppleUltra66ATA, IOATAStandardDriver )
35
36 static inline int rnddiv( int x, int y )
37 {
38 if ( x < 0 )
39 return 0;
40 else
41 return ( (x / y) + (( x % y ) ? 1 : 0) );
42 }
43
44
45 /*
46 *
47 *
48 */
49 bool AppleUltra66ATA::configure( IOService *forProvider, ATAControllerInfo *controllerInfo )
50 {
51 provider = forProvider;
52
53 if ( identifyController() == false )
54 {
55 return false;
56 }
57
58 ioMapATA = provider->mapDeviceMemoryWithIndex(0);
59 if ( ioMapATA == NULL ) return false;
60 ioBaseATA = (volatile UInt32 *)ioMapATA->getVirtualAddress();
61
62 ioMapDMA = provider->mapDeviceMemoryWithIndex(1);
63 if ( ioMapDMA == NULL ) return false;
64 ioBaseDMA = (volatile IODBDMAChannelRegisters *)ioMapDMA->getVirtualAddress();
65
66 dmaDescriptors = (IODBDMADescriptor *)kalloc(page_size);
67 if ( dmaDescriptors == 0 )
68 {
69 return false;
70 }
71
72 dmaDescriptorsPhys = (UInt32) pmap_extract(kernel_pmap, (vm_offset_t) dmaDescriptors);
73
74 if ( (UInt32)dmaDescriptors & (page_size - 1) )
75 {
76 IOLog("AppleUltra66ATA::%s() - DMA Descriptor memory not page aligned!!", __FUNCTION__);
77 return false;
78 }
79
80 bzero( dmaDescriptors, page_size );
81
82 numDescriptors = page_size/sizeof(IODBDMADescriptor);
83
84 dmaMemoryCursor = IOBigMemoryCursor::withSpecification( 64*1024-2, 0xffffffff );
85 if ( dmaMemoryCursor == NULL )
86 {
87 return false;
88 }
89
90 bitBucketAddr = IOMalloc(32);
91 if ( bitBucketAddr == 0 )
92 {
93 return false;
94 }
95 bitBucketAddrPhys = (UInt32) pmap_extract(kernel_pmap, (vm_offset_t) (((UInt32)bitBucketAddr + 0xf) & ~0x0f));
96
97 interruptEventSource = IOInterruptEventSource::interruptEventSource( (OSObject *) this,
98 (IOInterruptEventAction) &AppleUltra66ATA::interruptOccurred,
99 (IOService *) provider,
100 (int) 0 );
101
102 if ( interruptEventSource == NULL )
103 {
104 return false;
105 }
106
107 disableControllerInterrupts();
108
109 getWorkLoop()->addEventSource( interruptEventSource );
110
111 controllerInfo->maxDevicesSupported = 2;
112 controllerInfo->devicePrivateDataSize = 0;
113 controllerInfo->commandPrivateDataSize = 0;
114 controllerInfo->disableCancelCommands = false;
115
116 return true;
117 }
118
119
120 /*
121 *
122 *
123 */
124 bool AppleUltra66ATA::identifyController()
125 {
126 OSData *compatibleEntry, *modelEntry;
127
128 do
129 {
130 controllerType = kControllerTypeDBDMAVersion1;
131
132 compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "compatible" ) );
133 if ( compatibleEntry == 0 ) break;
134
135 if ( compatibleEntry->isEqualTo( "keylargo-ata", sizeof("keylargo-ata")-1 ) == true )
136 {
137 controllerType = kControllerTypeDBDMAVersion2;
138
139 modelEntry = OSDynamicCast( OSData, provider->getProperty("model") );
140 if ( modelEntry == 0 ) break;
141
142 if ( modelEntry->isEqualTo( "ata-4", sizeof("ata-4")-1 ) == true )
143 {
144 controllerType = kControllerTypeUltra66DBDMA;
145 }
146 }
147 } while ( 0 );
148
149 return true;
150 }
151
152
153 /*
154 *
155 *
156 */
157 bool AppleUltra66ATA::calculateTiming( UInt32 deviceNum, ATATiming *pTiming )
158 {
159 bool rc = false;
160
161 switch ( controllerType )
162 {
163 case kControllerTypeDBDMAVersion1:
164 case kControllerTypeDBDMAVersion2:
165 switch ( pTiming->timingProtocol )
166 {
167 case kATATimingPIO:
168 rc = calculatePIOTiming( deviceNum, pTiming );
169 break;
170
171 case kATATimingDMA:
172 rc = calculateDMATiming( deviceNum, pTiming );
173 break;
174
175 default:
176 ;
177 }
178 break;
179
180 case kControllerTypeUltra66DBDMA:
181 switch ( pTiming->timingProtocol )
182 {
183 case kATATimingPIO:
184 rc = calculateUltra66PIOTiming( deviceNum, pTiming );
185 break;
186
187 case kATATimingDMA:
188 rc = calculateUltra66DMATiming( deviceNum, pTiming );
189 break;
190
191 case kATATimingUltraDMA66:
192 rc = calculateUltra66UDMATiming( deviceNum, pTiming );
193 break;
194
195 default:
196 ;
197 }
198 break;
199
200 default:
201 ;
202 }
203
204 return rc;
205 }
206
207
208 /*
209 *
210 *
211 */
212 bool AppleUltra66ATA::calculatePIOTiming( UInt32 unitNum, ATATiming *pTiming )
213 {
214 int accessTime;
215 int accessTicks;
216 int recTime;
217 int recTicks;
218 int cycleTime;
219
220 /*
221 * Calc PIO access time >= minDataAccess in SYSCLK increments
222 */
223 accessTicks = rnddiv(pTiming->minDataAccess, kATASysClkNS);
224 /*
225 * Hardware limits access times to >= 120 ns
226 */
227 accessTicks -= kATAPioAccessBase;
228 if (accessTicks < kATAPioAccessMin )
229 {
230 accessTicks = kATAPioAccessMin;
231 }
232 accessTime = (accessTicks + kATAPioAccessBase) * kATASysClkNS;
233
234 /*
235 * Calc recovery time in SYSCLK increments based on time remaining in cycle
236 */
237 recTime = pTiming->minDataCycle - accessTime;
238 recTicks = rnddiv( recTime, kATASysClkNS );
239 /*
240 * Hardware limits recovery time to >= 150ns
241 */
242 recTicks -= kATAPioRecoveryBase;
243 if ( recTicks < kATAPioRecoveryMin )
244 {
245 recTicks = kATAPioRecoveryMin;
246 }
247
248 cycleTime = (recTicks + kATAPioRecoveryBase + accessTicks + kATAPioAccessBase) * kATASysClkNS;
249
250 ideTimingWord[unitNum] &= ~0x7ff;
251 ideTimingWord[unitNum] |= accessTicks | (recTicks << 5);
252
253 #if 0
254 IOLog("AppleUltra66ATA::%s() Unit %1d PIO Requested Timings: Access: %3dns Cycle: %3dns \n\r",
255 __FUNCTION__, (int)unitNum, (int)pTiming->minDataAccess, (int)pTiming->minDataCycle);
256 IOLog("AppleUltra66ATA::%s() PIO Actual Timings: Access: %3dns Cycle: %3dns\n\r",
257 __FUNCTION__, accessTime, cycleTime );
258 #endif
259
260 return true;
261 }
262
263
264 /*
265 *
266 *
267 */
268 bool AppleUltra66ATA::calculateDMATiming( UInt32 unitNum, ATATiming *pTiming )
269 {
270 int accessTime;
271 int accessTicks;
272 int recTime;
273 int recTicks;
274 int cycleTime;
275 int cycleTimeOrig;
276 int halfTick = 0;
277
278 /*
279 * Calc DMA access time >= minDataAccess in SYSCLK increments
280 */
281
282 /*
283 * OHare II erata - Cant handle write cycle times below 150ns
284 */
285 cycleTimeOrig = pTiming->minDataCycle;
286 #if 0
287 if ( IsPowerStar() )
288 {
289 if ( cycleTimeOrig < 150 ) pTiming->minDataCycle = 150;
290 }
291 #endif
292
293 accessTicks = rnddiv(pTiming->minDataAccess, kATASysClkNS);
294
295 accessTicks -= kATADmaAccessBase;
296 if ( accessTicks < kATADmaAccessMin )
297 {
298 accessTicks = kATADmaAccessMin;
299 }
300 accessTime = (accessTicks + kATADmaAccessBase) * kATASysClkNS;
301
302 /*
303 * Calc recovery time in SYSCLK increments based on time remaining in cycle
304 */
305 recTime = pTiming->minDataCycle - accessTime;
306 recTicks = rnddiv( recTime, kATASysClkNS );
307
308 recTicks -= kATADmaRecoveryBase;
309 if ( recTicks < kATADmaRecoveryMin )
310 {
311 recTicks = kATADmaRecoveryMin;
312 }
313 cycleTime = (recTicks + kATADmaRecoveryBase + accessTicks + kATADmaAccessBase) * kATASysClkNS;
314
315 /*
316 * If our calculated access time is at least SYSCLK/2 > than what the disk requires,
317 * see if selecting the 1/2 Clock option will help. This adds SYSCLK/2 to
318 * the access time and subtracts SYSCLK/2 from the recovery time.
319 *
320 * By setting the H-bit and subtracting one from the current access tick count,
321 * we are reducing the current access time by SYSCLK/2 and the current recovery
322 * time by SYSCLK/2. Now, check if the new cycle time still meets the disk's requirements.
323 */
324 if ( controllerType == kControllerTypeDBDMAVersion1 )
325 {
326 if ( (accessTicks > kATADmaAccessMin) && ((UInt32)(accessTime - kATASysClkNS/2) >= pTiming->minDataAccess) )
327 {
328 if ( (UInt32)(cycleTime - kATASysClkNS) >= pTiming->minDataCycle )
329 {
330 halfTick = 1;
331 accessTicks--;
332 accessTime -= kATASysClkNS/2;
333 cycleTime -= kATASysClkNS;
334 }
335 }
336 }
337
338 ideTimingWord[unitNum] &= ~0xffff800;
339 ideTimingWord[unitNum] |= (accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
340
341 #if 0
342 IOLog("AppleUltra66ATA::%s() Unit %1d DMA Requested Timings: Access: %3dns Cycle: %3dns \n\r",
343 __FUNCTION__, (int)unitNum, (int)pTiming->minDataAccess, (int)cycleTimeOrig);
344 IOLog("AppleUltra66ATA::%s() DMA Actual Timings: Access: %3dns Cycle: %3dns\n\r",
345 __FUNCTION__, accessTime, cycleTime );
346 IOLog("AppleUltra66ATA::%s() Ide DMA Timings = %08lx\n\r", __FUNCTION__, ideTimingWord[unitNum] );
347 #endif
348
349 return true;
350 }
351
352
353 /*
354 *
355 *
356 */
357 bool AppleUltra66ATA::calculateUltra66PIOTiming( UInt32 unitNum, ATATiming *pTiming )
358 {
359 int accessTime;
360 int accessTicks;
361 int recTime;
362 int recTicks;
363 int cycleTime;
364
365 /*
366 * Calc PIO access time >= pioAccessTime in SYSCLK increments
367 */
368 accessTicks = rnddiv(pTiming->minDataAccess * 1000, kATAUltra66ClockPS );
369 accessTime = accessTicks * kATAUltra66ClockPS;
370
371 /*
372 * Calc recovery time in SYSCLK increments based on time remaining in cycle
373 */
374 recTime = pTiming->minDataCycle * 1000 - accessTime;
375 recTicks = rnddiv( recTime, kATAUltra66ClockPS );
376
377 cycleTime = (recTicks + accessTicks ) * kATAUltra66ClockPS;
378
379 ideTimingWord[unitNum] &= ~0xe00003ff;
380 ideTimingWord[unitNum] |= accessTicks | (recTicks << 5);
381
382 #if 0
383 IOLog("AppleUltra66ATA::%s() Unit %1d PIO Requested Timings: Access: %3dns Cycle: %3dns \n\r",
384 __FUNCTION__, (int)unitNum, (int)pTiming->minDataAccess, (int)pTiming->minDataCycle);
385 IOLog("AppleUltra66ATA::%s() PIO Actual Timings: Access: %3dns Cycle: %3dns\n\r",
386 __FUNCTION__, accessTime / 1000, cycleTime / 1000 );
387 IOLog("AppleUltra66ATA::%s() Ide PIO Timings = %08lx\n\r", __FUNCTION__, ideTimingWord[unitNum] );
388 #endif
389
390 return true;
391 }
392
393
394 /*
395 *
396 *
397 */
398 bool AppleUltra66ATA::calculateUltra66DMATiming( UInt32 unitNum, ATATiming *pTiming )
399 {
400 int accessTime;
401 int accessTicks;
402 int recTime;
403 int recTicks;
404 int cycleTime;
405
406 /*
407 * Calc DMA access time >= dmaAccessTime in SYSCLK increments
408 */
409 accessTicks = rnddiv(pTiming->minDataAccess * 1000, kATAUltra66ClockPS);
410 accessTime = accessTicks * kATAUltra66ClockPS;
411
412 /*
413 * Calc recovery time in SYSCLK increments based on time remaining in cycle
414 */
415 recTime = pTiming->minDataCycle * 1000 - accessTime;
416 recTicks = rnddiv( recTime, kATAUltra66ClockPS );
417
418 cycleTime = (recTicks + accessTicks) * kATAUltra66ClockPS;
419
420 ideTimingWord[unitNum] &= ~0x001ffc00;
421 ideTimingWord[unitNum] |= (accessTicks | (recTicks << 5)) << 10;
422
423 #if 0
424 IOLog("AppleUltra66ATA::%s() Unit %1d DMA Requested Timings: Access: %3dns Cycle: %3dns \n\r",
425 __FUNCTION__, (int)unitNum, (int)pTiming->minDataAccess, (int)pTiming->minDataCycle);
426 IOLog("AppleUltra66ATA::%s() DMA Actual Timings: Access: %3dns Cycle: %3dns\n\r",
427 __FUNCTION__, accessTime / 1000, cycleTime / 1000 );
428 IOLog("AppleUltra66ATA::%s() Ide DMA Timings = %08lx\n\r", __FUNCTION__, ideTimingWord[unitNum] );
429 #endif
430
431 return true;
432 }
433
434
435 /*
436 *
437 *
438 */
439 bool AppleUltra66ATA::calculateUltra66UDMATiming( UInt32 unitNum, ATATiming *pTiming )
440 {
441 int rdyToPauseTicks;
442 int rdyToPauseTime;
443 int cycleTime;
444 int cycleTicks;
445
446 /*
447 * Ready to Pause delay in PCI_66_CLOCK / 2 increments
448 */
449 rdyToPauseTicks = rnddiv(pTiming->minDataAccess * 1000, kATAUltra66ClockPS);
450 rdyToPauseTime = rdyToPauseTicks * kATAUltra66ClockPS;
451
452 /*
453 * Calculate cycle time in PCI_66_CLOCK / 2 increments
454 */
455 cycleTicks = rnddiv(pTiming->minDataCycle * 1000, kATAUltra66ClockPS);
456 cycleTime = cycleTicks * kATAUltra66ClockPS;
457
458 ideTimingWord[unitNum] &= ~0x1ff00000;
459 ideTimingWord[unitNum] |= ((rdyToPauseTicks << 5) | (cycleTicks << 1) | 1) << 20;
460
461 #if 0
462 IOLog("AppleUltra66ATA::%s() Unit %1d UDMA66 Requested Timings: ReadyToPause: %3dns Cycle: %3dns \n\r",
463 __FUNCTION__, (int)unitNum, (int)pTiming->minDataAccess, (int)pTiming->minDataCycle);
464 IOLog("AppleUltra66ATA::%s() UDMA66 Actual Timings: ReadyToPause: %3dns Cycle: %3dns\n\r",
465 __FUNCTION__, rdyToPauseTime / 1000, cycleTime / 1000 );
466 IOLog("AppleUltra66ATA::%s() Ide DMA Timings = %08lx\n\r", __FUNCTION__, ideTimingWord[unitNum] );
467 #endif
468
469 return true;
470 }
471
472
473 /*
474 *
475 *
476 */
477 void AppleUltra66ATA::newDeviceSelected( IOATAStandardDevice *newDevice )
478 {
479 OSWriteSwapInt32( ioBaseATA, 0x200, ideTimingWord[newDevice->getUnit()] );
480 eieio();
481 }
482
483
484 /*
485 *
486 *
487 */
488 bool AppleUltra66ATA::selectTiming( UInt32 unitNum, ATATimingProtocol timingProtocol )
489 {
490 if ( controllerType == kControllerTypeUltra66DBDMA )
491 {
492 switch ( timingProtocol )
493 {
494 case kATATimingUltraDMA66:
495 ideTimingWord[unitNum] |= 0x00100000;
496 break;
497 case kATATimingDMA:
498 ideTimingWord[unitNum] &= ~0x00100000;
499 break;
500 default:
501 ;
502 }
503 }
504 return true;
505 }
506
507 /*
508 *
509 *
510 */
511 bool AppleUltra66ATA::programDma( IOATAStandardCommand *cmd )
512 {
513 IOMemoryDescriptor *memoryDesc;
514 IODBDMADescriptor *dmaDesc;
515 UInt32 dmaCmd;
516 bool isWrite;
517 IOPhysicalSegment physSeg;
518 IOByteCount offset;
519 UInt32 i;
520
521 IODBDMAReset( ioBaseDMA );
522
523 cmd->getPointers( &memoryDesc, &dmaReqLength, &isWrite );
524
525 if ( dmaReqLength == 0 )
526 {
527 return true;
528 }
529
530 offset = 0;
531
532 dmaCmd = (isWrite == true) ? kdbdmaOutputMore : kdbdmaInputMore;
533 dmaDesc = dmaDescriptors;
534
535 for ( i = 0; i < numDescriptors; i++, dmaDesc++ )
536 {
537 if ( dmaMemoryCursor->getPhysicalSegments( memoryDesc, offset, &physSeg, 1 ) != 1 )
538 {
539 break;
540 }
541
542 IOMakeDBDMADescriptor( dmaDesc,
543 dmaCmd,
544 kdbdmaKeyStream0,
545 kdbdmaIntNever,
546 kdbdmaBranchNever,
547 kdbdmaWaitNever,
548 physSeg.length,
549 physSeg.location );
550 offset += physSeg.length;
551 }
552
553 if ( i == numDescriptors )
554 {
555 return false;
556 }
557
558 /*
559 * Note: ATAPI always transfers even byte-counts. Send the extra byte to/from the bit-bucket
560 * if the requested transfer length is odd.
561 */
562 if ( dmaReqLength & 1 )
563 {
564 i++;
565 IOMakeDBDMADescriptor( dmaDesc++,
566 dmaCmd,
567 kdbdmaKeyStream0,
568 kdbdmaIntNever,
569 kdbdmaBranchNever,
570 kdbdmaWaitNever,
571 1,
572 bitBucketAddrPhys );
573 }
574
575
576 if ( i == numDescriptors )
577 {
578 return false;
579 }
580
581
582 IOMakeDBDMADescriptor( dmaDesc,
583 kdbdmaStop,
584 kdbdmaKeyStream0,
585 kdbdmaIntNever,
586 kdbdmaBranchNever,
587 kdbdmaWaitNever,
588 0,
589 0 );
590
591 IOSetDBDMACommandPtr( ioBaseDMA, dmaDescriptorsPhys );
592
593
594 return true;
595 }
596
597
598 /*
599 *
600 *
601 */
602 bool AppleUltra66ATA::startDma( IOATAStandardCommand * )
603 {
604 if ( dmaReqLength != 0 )
605 {
606 IODBDMAContinue( ioBaseDMA );
607 }
608 return true;
609 }
610
611
612 /*
613 *
614 *
615 */
616 bool AppleUltra66ATA::stopDma( IOATAStandardCommand *, UInt32 *transferCount )
617 {
618 UInt32 i;
619 UInt32 ccResult;
620 UInt32 byteCount = 0;
621
622 *transferCount = 0;
623
624 if ( dmaReqLength == 0 )
625 {
626 return true;
627 }
628
629 IODBDMAStop( ioBaseDMA );
630
631 for ( i=0; i < numDescriptors; i++ )
632 {
633 ccResult = IOGetCCResult( &dmaDescriptors[i] );
634
635 if ( (ccResult & (kdbdmaStatusActive | kdbdmaStatusDead)) == 0 )
636 {
637 break;
638 }
639 byteCount += (IOGetCCOperation( &dmaDescriptors[i] ) & kdbdmaReqCountMask) - (ccResult & kdbdmaResCountMask);
640 }
641
642 *transferCount = byteCount;
643
644 return true;
645 }
646
647 /*
648 *
649 *
650 */
651 bool AppleUltra66ATA::resetDma()
652 {
653 IODBDMAReset( ioBaseDMA );
654 return true;
655 }
656
657 /*
658 *
659 *
660 */
661 bool AppleUltra66ATA::checkDmaActive()
662 {
663 return ((IOGetDBDMAChannelStatus( ioBaseDMA ) & kdbdmaActive) != 0);
664 }
665
666
667 /*
668 *
669 *
670 */
671 void AppleUltra66ATA::disableControllerInterrupts()
672 {
673 interruptEventSource->disable();
674 }
675
676 /*
677 *
678 *
679 */
680 void AppleUltra66ATA::enableControllerInterrupts()
681 {
682 interruptEventSource->enable();
683 }
684
685 /*
686 *
687 *
688 */
689 void AppleUltra66ATA::free()
690 {
691 if ( interruptEventSource != 0 )
692 {
693 interruptEventSource->disable();
694 interruptEventSource->release();
695 }
696
697 if ( ioMapATA != 0 )
698 {
699 ioMapATA->release();
700 }
701
702 if ( ioMapDMA != 0 )
703 {
704 ioMapDMA->release();
705 }
706
707 if ( bitBucketAddr != 0 )
708 {
709 IOFree( bitBucketAddr, 32 );
710 }
711
712 if ( dmaDescriptors != 0 )
713 {
714 kfree( (vm_offset_t)dmaDescriptors, page_size );
715 }
716 }
717
718 /*
719 *
720 *
721 */
722 void AppleUltra66ATA::writeATAReg( UInt32 regIndex, UInt32 regValue )
723 {
724 regIndex += (regIndex >= kATARegDeviceControl ) ? (kATACS3RegBase - kATARegDeviceControl + 6) : 0;
725
726 if ( regIndex )
727 {
728 *((volatile UInt8 *)ioBaseATA + (regIndex<<4)) = regValue;
729 }
730 else
731 {
732 *(volatile UInt16 *)ioBaseATA = regValue;
733 }
734 eieio();
735 }
736
737 UInt32 AppleUltra66ATA::readATAReg( UInt32 regIndex )
738 {
739 regIndex += (regIndex >= kATARegAltStatus ) ? (kATACS3RegBase - kATARegAltStatus + 6) : 0;
740
741 if ( regIndex )
742 {
743 return *((volatile UInt8 *)ioBaseATA + (regIndex<<4));
744 }
745 else
746 {
747 return *(volatile UInt16 *)ioBaseATA;
748 }
749 }