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#include <IOKit/sbp2/IOFireWireSBP2ManagementORB.h>
24#include <IOKit/sbp2/IOFireWireSBP2LUN.h>
25
26#define FIREWIREPRIVATE
27#include <IOKit/firewire/IOFireWireController.h>
28#undef FIREWIREPRIVATE
29
30#include <IOKit/firewire/IOConfigDirectory.h>
31
32#include "FWDebugging.h"
33
34extern const OSSymbol *gUnit_Characteristics_Symbol;
35extern const OSSymbol *gManagement_Agent_Offset_Symbol;
36
37OSDefineMetaClassAndStructors( IOFireWireSBP2ManagementORB, IOFWCommand );
38
39//OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 0);
40OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 1);
41OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 2);
42OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 3);
43OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 4);
44OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 5);
45OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 6);
46OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 7);
47OSMetaClassDefineReservedUnused(IOFireWireSBP2ManagementORB, 8);
48
49// init
50//
51//
52
53bool IOFireWireSBP2ManagementORB::initWithLUN( IOFireWireSBP2LUN * lun, void * refCon, FWSBP2ManagementCallback completion )
54{
55    bool res = true;
56    IOReturn status	= kIOReturnSuccess;
57
58	// we want the expansion data member to be zeroed if it's available
59	// so create and zero in a local then assign to the member when were done
60
61	ExpansionData * exp_data = (ExpansionData*) IOMalloc( sizeof(ExpansionData) );
62	if( !exp_data )
63	{
64		return false;
65	}
66
67	bzero( exp_data, sizeof(ExpansionData) );
68
69	fExpansionData = exp_data;
70
71    // store LUN & Unit
72    fLUN = lun;
73	fUnit = fLUN->getFireWireUnit();
74
75    fExpansionData->fInCriticalSection = false;
76
77    // init command fields
78    fControl = fUnit->getController();
79    fTimeout = 0;
80    fSync = false;
81
82    // set completion routine and refcon
83    fCompletionCallback = completion;
84    fCompletionRefCon = refCon;
85
86    // timer flags
87    fTimeoutTimerSet = false;
88
89    fStatusBlockAddressSpace 	= NULL;
90    fManagementORBAddressSpace	= NULL;
91    fWriteCommand 				= NULL;
92    fWriteCommandMemory 		= NULL;
93
94    fFunction 				= 0;
95    fResponseBuf 			= NULL;
96    fResponseLen 			= 0;
97    fResponseAddressSpace 	= NULL;
98    fResponseAddress		= NULL;
99
100	fCompleting = false;
101
102    // init super
103    res = IOFWCommand::initWithController( fControl );
104
105    // scan unit
106    if( res )
107    {
108        status = getUnitInformation();
109        res = ( status == kIOReturnSuccess );
110    }
111
112    // allocate resources
113    if( res )
114    {
115        status = allocateResources();
116        res = ( status == kIOReturnSuccess );
117    }
118
119    return res;
120}
121
122/////////////////////////////////////////////////////////////////////
123// getUnitInformation
124//
125// gathers SBP2 specific information from unit's ROM
126// specifically it reads, the management agent, and unit dependent
127// characterisitcs
128
129IOReturn IOFireWireSBP2ManagementORB::getUnitInformation( void )
130{
131    IOReturn			status = kIOReturnSuccess;
132    UInt32				unitCharacteristics = 0;
133	OSObject *			prop = NULL;
134
135	prop = fLUN->getProperty( gManagement_Agent_Offset_Symbol );
136	if( prop == NULL )
137		status = kIOReturnError;
138
139	if( status == kIOReturnSuccess )
140	{
141		fManagementOffset = ((OSNumber*)prop)->unsigned32BitValue();
142	}
143
144    FWKLOG( ("IOFireWireSBP2ManagementORB<%p> : status = %d, fManagementOffset = %d\n", this, status, fManagementOffset) );
145
146    //
147    // find unit characteristics
148    //
149
150	if( status == kIOReturnSuccess )
151	{
152		prop = fLUN->getProperty( gUnit_Characteristics_Symbol );
153		if( prop == NULL )
154			status = kIOReturnError;
155	}
156
157	if( status == kIOReturnSuccess )
158	{
159		unitCharacteristics = ((OSNumber*)prop)->unsigned32BitValue();
160
161        // extract management timeout, max ORB size, max command block size
162
163        fManagementTimeout = ((unitCharacteristics >> 8) & 0xff) * 500;   // in milliseconds
164   }
165
166    return status;
167}
168
169// allocateResources
170//
171//
172
173IOReturn IOFireWireSBP2ManagementORB::allocateResources( void )
174{
175    IOReturn					status = kIOReturnSuccess;
176
177    //
178    // allocate and register an address space for the management ORB
179    //
180
181	FWAddress host_address(0,0);
182
183    if( status == kIOReturnSuccess )
184    {
185        fManagementORBAddressSpace = IOFWPseudoAddressSpace::simpleRead( fControl, &host_address,
186                                                                         sizeof(FWSBP2TaskManagementORB), & fManagementORB );
187    	if ( fManagementORBAddressSpace == NULL )
188        	status = kIOReturnNoMemory;
189    }
190
191    if( status == kIOReturnSuccess )
192    {
193		fManagementORBAddress.nodeID = OSSwapHostToBigInt16(host_address.nodeID);
194		fManagementORBAddress.addressHi = OSSwapHostToBigInt16(host_address.addressHi);
195		fManagementORBAddress.addressLo = OSSwapHostToBigInt32(host_address.addressLo);
196
197        status = fManagementORBAddressSpace->activate();
198    }
199
200    //
201    // allocate and register an address space for the status block
202    //
203
204     if( status == kIOReturnSuccess )
205    {
206        fStatusBlockAddressSpace = fUnit->createPseudoAddressSpace( &fStatusBlockAddress, sizeof(FWSBP2StatusBlock),
207                                                                    NULL, IOFireWireSBP2ManagementORB::statusBlockWriteStatic,
208                                                                    this );
209        if ( fStatusBlockAddressSpace == NULL )
210            status = kIOReturnNoMemory;
211    }
212
213    if( status == kIOReturnSuccess )
214    {
215        status = fStatusBlockAddressSpace->activate();
216    }
217
218    //
219    // create command for writing the management agent
220    //
221
222    if( status == kIOReturnSuccess )
223    {
224        fWriteCommandMemory = IOMemoryDescriptor::withAddress( &fManagementORBAddress, 8, kIODirectionOut );
225    	if( fWriteCommandMemory == NULL )
226    		status = kIOReturnNoMemory;
227    }
228
229    if( status == kIOReturnSuccess )
230    {
231        fWriteCommand = fUnit->createWriteCommand( FWAddress(0x0000ffff, 0xf0000000 + (fManagementOffset << 2)),
232                                                   fWriteCommandMemory,
233                                                   IOFireWireSBP2ManagementORB::writeCompleteStatic, this, true );
234        if( fWriteCommand == NULL )
235        	status = kIOReturnNoMemory;
236    }
237
238    if( status == kIOReturnSuccess )
239    {
240        fManagementORB.statusFIFOAddressHi = OSSwapHostToBigInt32((fStatusBlockAddress.nodeID << 16) | fStatusBlockAddress.addressHi);
241        fManagementORB.statusFIFOAddressLo = OSSwapHostToBigInt32(fStatusBlockAddress.addressLo);
242    }
243
244	if( status == kIOReturnSuccess )
245	{
246		fTimeoutCommand = fControl->createDelayedCmd( fManagementTimeout * 1000,
247                                                      IOFireWireSBP2ManagementORB::handleTimeoutStatic, this );
248		if( !fTimeoutCommand )
249			status = kIOReturnNoMemory;
250	}
251
252    return status;
253}
254
255// free
256//
257//
258
259void IOFireWireSBP2ManagementORB::free( void )
260{
261    IOReturn						status = kIOReturnSuccess;
262
263	removeManagementORB( this );
264
265    // cancel timer
266    if( fTimeoutTimerSet )
267        fTimeoutCommand->cancel(kIOReturnAborted);
268
269	if( fTimeoutCommand )
270		fTimeoutCommand->release();
271
272    //
273    // deallocate management orb address space
274    //
275
276    if( fManagementORBAddressSpace != NULL && status == kIOReturnSuccess )
277    {
278        fManagementORBAddressSpace->deactivate();
279        fManagementORBAddressSpace->release();
280    }
281
282    //
283    // deallocate status block address space
284    //
285
286    if( fStatusBlockAddressSpace != NULL && status == kIOReturnSuccess )
287    {
288        fStatusBlockAddressSpace->deactivate();
289        fStatusBlockAddressSpace->release();
290    }
291
292	if( fResponseMap != NULL )
293	{
294		fResponseMap->release();
295		fResponseMap = NULL;
296	}
297
298    //
299    // deallocate response address space
300    //
301
302    if( fResponseAddressSpace != NULL && status == kIOReturnSuccess )
303    {
304        fResponseAddressSpace->deactivate();
305        fResponseAddressSpace->release();
306    }
307
308    //
309    // release command for writing the management agent
310    //
311
312    if( fWriteCommand != NULL )
313        fWriteCommand->release();
314
315    if( fWriteCommandMemory != NULL )
316        fWriteCommandMemory->release();
317
318	//
319	// free expansion data
320	//
321
322	if( fExpansionData )
323	{
324		IOFree( fExpansionData, sizeof(ExpansionData) );
325		fExpansionData = NULL;
326	}
327
328    IOFWCommand::free();
329}
330
331void IOFireWireSBP2ManagementORB::release() const
332{
333	if( getRetainCount() >= 2 )
334		IOFWCommand::release(2);
335}
336
337//
338// accessors
339//
340
341// get / set command function
342
343IOReturn IOFireWireSBP2ManagementORB::setCommandFunction( UInt32 function )
344{
345    // disallow login/reconnect/password/logout functions
346    if( function == 0 || function == 3 || function == 4 || function == 7 )
347    {
348        return kIOReturnBadArgument;
349    }
350    else
351    {
352        fFunction = function;
353
354        fManagementORB.options &= OSSwapHostToBigInt16(0xfff0);
355        fManagementORB.options |= OSSwapHostToBigInt16(function | 0x8000);  // new and notify
356    }
357
358    return kIOReturnSuccess;
359}
360
361UInt32 IOFireWireSBP2ManagementORB::getCommandFunction( void )
362{
363    return fFunction;
364}
365
366// get / set managee command
367
368void IOFireWireSBP2ManagementORB::setManageeCommand( OSObject * command )
369{
370    fManageeCommand = command;
371}
372
373OSObject* IOFireWireSBP2ManagementORB::getManageeCommand( void )
374{
375    return fManageeCommand;
376}
377
378// get / set response buffer
379
380IOReturn IOFireWireSBP2ManagementORB::setResponseBuffer( void * buf, UInt32 len )
381{
382    IOReturn						status 			= kIOReturnSuccess;
383
384    //
385    // deallocate old response address space
386    //
387
388    if( fResponseAddressSpace != NULL )
389    {
390        fResponseAddressSpace->deactivate();
391        fResponseAddressSpace->release();
392    }
393
394	fResponseBuf = buf;
395	fResponseLen = len;
396
397    //
398    // allocate and register an address space for the response
399    //
400
401    if( status == kIOReturnSuccess && buf != NULL && len != 0 )
402    {
403        fResponseAddressSpace = IOFWPseudoAddressSpace::simpleRW( fControl,
404																&fResponseAddress,
405                                                                      len, buf );
406        if ( fResponseAddressSpace == NULL )
407            status = kIOReturnNoMemory;
408
409		if( status == kIOReturnSuccess )
410		{
411			status = fResponseAddressSpace->activate();
412		}
413    }
414
415    if( status != kIOReturnSuccess )
416    {
417        fResponseBuf = NULL;
418        fResponseLen = 0;
419    }
420
421    return status;
422}
423
424void IOFireWireSBP2ManagementORB::getResponseBuffer( void ** buf, UInt32 * len )
425{
426    *buf = fResponseBuf;
427    *len = fResponseLen;
428}
429
430IOReturn IOFireWireSBP2ManagementORB::setResponseBuffer
431								( IOMemoryDescriptor * desc )
432{
433    IOReturn				status 			= kIOReturnSuccess;
434    void * 					buf = NULL;
435    UInt32 					len = 0;
436
437    //
438    // deallocate old response address space
439    //
440
441    if( fResponseAddressSpace != NULL )
442    {
443        fResponseAddressSpace->deactivate();
444        fResponseAddressSpace->release();
445    }
446
447	if( fResponseMap != NULL )
448	{
449		fResponseMap->release();
450	}
451
452	if( desc != NULL )
453    {
454        len = desc->getLength();
455
456		fResponseMap = desc->map();
457		if( fResponseMap != NULL )
458		{
459			buf = (void*)fResponseMap->getVirtualAddress();
460		}
461     }
462
463	fResponseBuf = buf;
464	fResponseLen = len;
465
466    //
467    // allocate and register an address space for the response
468    //
469
470    if( status == kIOReturnSuccess && desc != NULL && buf != NULL && len != 0 )
471    {
472        fResponseAddressSpace = IOFWPseudoAddressSpace::simpleRW( fControl,
473																  &fResponseAddress,
474																  desc );
475        if ( fResponseAddressSpace == NULL )
476            status = kIOReturnNoMemory;
477
478		if( status == kIOReturnSuccess )
479		{
480			status = fResponseAddressSpace->activate();
481		}
482    }
483
484    if( status != kIOReturnSuccess )
485    {
486        fResponseBuf = NULL;
487        fResponseLen = 0;
488    }
489
490    return status;
491}
492
493
494//
495// command execution
496//
497
498IOReturn IOFireWireSBP2ManagementORB::execute( void )
499{
500    FWKLOG( ( "IOFireWireSBP2ManagementORB<%p> : execute\n", this ) );
501	IOReturn status = kIOReturnSuccess;
502
503    IOFireWireSBP2Login * 	login 	= NULL;
504    IOFireWireSBP2ORB * 	orb 	= NULL;
505
506    switch( fFunction )
507    {
508        case kFWSBP2TargetReset:
509        case kFWSBP2LogicalUnitReset:
510        case kFWSBP2AbortTaskSet:
511            login = OSDynamicCast( IOFireWireSBP2Login, fManageeCommand );
512            if( login == NULL )
513                status = kIOReturnBadArgument;
514            break;
515
516        case kFWSBP2AbortTask:
517            orb = OSDynamicCast( IOFireWireSBP2ORB, fManageeCommand );
518            if( orb == NULL )
519                status = kIOReturnBadArgument;
520            else
521                login = orb->getLogin();
522            break;
523
524        case kFWSBP2QueryLogins:
525            break;
526
527        default:
528            status = kIOReturnBadArgument;
529    }
530
531    // set login ID for all transactions except query logins
532    if( status == kIOReturnSuccess && fFunction != kFWSBP2QueryLogins )
533    {
534        fManagementORB.loginID = OSSwapHostToBigInt16(login->getLoginID());
535    }
536
537    if( status == kIOReturnSuccess && fFunction == kFWSBP2AbortTask )
538    {
539        // set orb address
540        FWAddress address;
541        orb->getORBAddress( &address );
542        fManagementORB.orbOffsetHi = OSSwapHostToBigInt32(0x0000ffff & address.addressHi);
543        fManagementORB.orbOffsetLo = OSSwapHostToBigInt32(0xfffffffc & address.addressLo);
544
545        // abort orb
546		setORBToDummy( orb );
547    }
548
549    if( status == kIOReturnSuccess && fFunction == kFWSBP2QueryLogins )
550    {
551        FWSBP2QueryLoginsORB * queryLoginsORB = (FWSBP2QueryLoginsORB *)&fManagementORB;
552
553        queryLoginsORB->lun = OSSwapHostToBigInt16(fLUN->getLUNumber());
554        queryLoginsORB->queryResponseAddressHi = OSSwapHostToBigInt32(0x0000ffff & fResponseAddress.addressHi);
555        queryLoginsORB->queryResponseAddressLo = OSSwapHostToBigInt32(fResponseAddress.addressLo);
556        queryLoginsORB->queryResponseLength = OSSwapHostToBigInt16(fResponseLen);
557    }
558
559    if( status == kIOReturnSuccess )
560    {
561        fWriteCommand->reinit( FWAddress(0x0000ffff, 0xf0000000 + (fManagementOffset << 2)),
562                               fWriteCommandMemory,
563                               IOFireWireSBP2ManagementORB::writeCompleteStatic, this, true );
564
565		status = fLUN->getTarget()->beginIOCriticalSection();
566	}
567
568	if( status == kIOReturnSuccess )
569	{
570		fExpansionData->fInCriticalSection = true;
571		status = fWriteCommand->submit();
572	}
573
574    if( status != kIOReturnSuccess )
575    {
576		if( fExpansionData->fInCriticalSection )
577		{
578			fExpansionData->fInCriticalSection = false;
579			fLUN->getTarget()->endIOCriticalSection();
580		}
581
582		return status;
583    }
584
585    return kIOReturnBusy;    // this means we are now busy working on this command
586}
587
588//
589// write complete handler
590//
591
592void IOFireWireSBP2ManagementORB::writeCompleteStatic( void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd )
593{
594    ((IOFireWireSBP2ManagementORB*)refcon)->writeComplete( status, device, fwCmd );
595}
596
597void IOFireWireSBP2ManagementORB::writeComplete( IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd )
598{
599    FWKLOG( ( "IOFireWireSBP2ManagementORB<%p> : write complete\n", this ) );
600
601    if( status == kIOReturnSuccess )
602    {
603        // we wrote the management agent, now set a timer and wait for response & status
604        fTimeoutTimerSet = true;
605		if( fTimeoutCommand->submit() != kIOReturnSuccess )
606			fTimeoutTimerSet = false;
607    }
608    else
609        complete( status );  // complete with error
610
611}
612
613//
614// timeout handler
615//
616
617void IOFireWireSBP2ManagementORB::handleTimeoutStatic( void *refcon, IOReturn status, IOFireWireBus *bus, IOFWBusCommand *fwCmd )
618{
619    ((IOFireWireSBP2ManagementORB*)refcon)->handleTimeout( status, bus, fwCmd );
620}
621
622void IOFireWireSBP2ManagementORB::handleTimeout( IOReturn status, IOFireWireBus *bus, IOFWBusCommand *fwCmd )
623{
624    fTimeoutTimerSet = false;
625
626    if( status == kIOReturnTimeout )
627    {
628        FWKLOG( ( "IOFireWireSBP2ManagementORB<%p> : handle timeout\n", this ) );
629        complete( kIOReturnTimeout );
630    }
631}
632
633//
634// status block write handler
635//
636
637UInt32 IOFireWireSBP2ManagementORB::statusBlockWriteStatic( void *refcon, UInt16 nodeID, IOFWSpeed &speed, FWAddress addr,
638                                                            UInt32 len, const void *buf, IOFWRequestRefCon lockRead )
639{
640    return ((IOFireWireSBP2ManagementORB*)refcon)->statusBlockWrite( nodeID, addr, len, buf, lockRead );
641}
642
643UInt32 IOFireWireSBP2ManagementORB::statusBlockWrite( UInt16 nodeID, FWAddress addr, UInt32 len, const void *buf,
644                                                      IOFWRequestRefCon lockRead )
645{
646    // if timer isn't running we should not have been called.  don't panic...
647    if( !fTimeoutTimerSet )
648        return kFWResponseComplete;
649
650    // cancel timeout
651    fTimeoutCommand->cancel(kIOReturnAborted);
652
653	if( fFunction == kFWSBP2AbortTaskSet ||
654		fFunction == kFWSBP2TargetReset ||
655		fFunction == kFWSBP2LogicalUnitReset )
656	{
657		// all tasks aborted once these babies complete
658		fCompleting = true;
659		clearAllTasksInSet();
660		fCompleting = false;
661	}
662
663    // complete command
664    complete( kIOReturnSuccess );
665
666    return kFWResponseComplete;
667}
668
669//
670// suspendedNotify method
671//
672// called when a suspended message is received
673
674void IOFireWireSBP2ManagementORB::suspendedNotify( void )
675{
676    FWKLOG( ( "IOFireWireSBP2ManagementORB<%p> : suspendedNotify\n", this ) );
677
678	if( fStatus == kIOReturnBusy && !fCompleting )
679	{
680		if( fTimeoutTimerSet )
681		{
682			// cancel timeout
683			fTimeoutCommand->cancel( kIOReturnAborted );
684		}
685
686		// complete command
687		complete( kIOFireWireBusReset );
688	}
689}
690
691//
692// complete
693//
694
695IOReturn IOFireWireSBP2ManagementORB::complete( IOReturn state )
696{
697    state = IOFWCommand::complete( state );
698    FWKLOG( ( "IOFireWireSBP2ManagementORB<%p> : complete\n", this ) );
699
700	if( fExpansionData->fInCriticalSection )
701	{
702		fExpansionData->fInCriticalSection = false;
703		fLUN->getTarget()->endIOCriticalSection();
704	}
705
706	if( fCompletionCallback != NULL )
707        (*fCompletionCallback)(fCompletionRefCon, state, this);
708
709    return state;
710}
711
712// async ref handling for user client
713//
714//
715
716void IOFireWireSBP2ManagementORB::setAsyncCallbackReference( void * asyncRef )
717{
718     bcopy( asyncRef, fCallbackAsyncRef, sizeof(OSAsyncReference64) );
719}
720
721void IOFireWireSBP2ManagementORB::getAsyncCallbackReference( void * asyncRef )
722{
723    bcopy( fCallbackAsyncRef, asyncRef, sizeof(OSAsyncReference64) );
724}
725
726//////////////////////////////////////////////////////////////////////////////////////////
727// friend class wrappers
728
729// IOFireWireSBP2LUN friend class wrappers
730
731void IOFireWireSBP2ManagementORB::clearAllTasksInSet( void )
732{
733	fLUN->clearAllTasksInSet();
734}
735
736void IOFireWireSBP2ManagementORB::removeManagementORB( IOFireWireSBP2ManagementORB * orb )
737{
738	fLUN->removeManagementORB( orb );
739}
740
741// IOFireWireSBP2ORB friend class wrappers
742
743void IOFireWireSBP2ManagementORB::setORBToDummy( IOFireWireSBP2ORB * orb )
744{
745	orb->setToDummy();
746}
747