1/*
2 * Copyright (c) 1998-2001 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#include <IOKit/avc/IOFireWireAVCCommand.h>
23#include <IOKit/avc/IOFireWireAVCConsts.h>
24
25#include <IOKit/firewire/IOFireWireNub.h>
26#include <IOKit/firewire/IOFireWireController.h>
27#include <IOKit/IOSyncer.h>
28
29#if FIRELOG
30#import <IOKit/firewire/FireLog.h>
31#define FIRELOG_MSG(x) FireLog x
32#else
33#define FIRELOG_MSG(x) do {} while (0)
34#endif
35
36#define kInterimTimeout 10000000
37
38class IOFireWireAVCCommandInGen : public IOFireWireAVCCommand
39{
40    OSDeclareDefaultStructors(IOFireWireAVCCommandInGen)
41
42protected:
43    UInt32 fDummy;
44
45    virtual IOReturn	complete(IOReturn status);
46
47public:
48    virtual bool init(IOFireWireNub *device, UInt32 generation, const UInt8 * command, UInt32 cmdLen,
49                                                    UInt8 * response, UInt32 * responseLen);
50    virtual IOReturn reinit(IOFireWireNub *device, UInt32 generation, const UInt8 * command, UInt32 cmdLen,
51                                                    UInt8 * response, UInt32 * responseLen);
52
53private:
54    OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 0);
55    OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 1);
56    OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 2);
57    OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 3);
58};
59
60
61OSDefineMetaClassAndStructors(IOFireWireAVCCommand, IOFWCommand)
62//OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 0);
63OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 1);
64OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 2);
65OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 3);
66
67/*
68 Possible states for an AVCCommand:
69
70 Not submitted: fTimeout = 0, fWriteNodeID = kFWBadNodeID, fStatus != Busy().
71 Write submitted: fTimeout = 0, fWriteNodeID = kFWBadNodeID, fStatus = kIOReturnBusy
72 Write complete: fTimeout = 250000, fWriteNodeID = device node, fStatus = kIOReturnBusy
73 Interim received: fTimeout = kInterimTimeout, fWriteNodeID = device node, fStatus = kIOReturnBusy
74 Complete: fStatus != Busy()
75*/
76
77void IOFireWireAVCCommand::writeDone(void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd)
78{
79    IOFireWireAVCCommand *me = (IOFireWireAVCCommand *)refcon;
80
81    FIRELOG_MSG(("IOFireWireAVCCommand::writeDone (this=0x%08X)\n",me));
82
83	if(status == kIOReturnSuccess) {
84        // Store current node and generation
85        if(device)
86            device->getNodeIDGeneration(me->fWriteGen, me->fWriteNodeID);
87
88        // Start timer for command response
89        me->fTimeout = 250000;
90        me->updateTimer();
91    }
92    else {
93        IOLog("Write for %p done, status %x\n", refcon, status);
94        me->complete(status);
95    }
96}
97
98UInt32 IOFireWireAVCCommand::handleResponseWithSimpleMatching(UInt16 nodeID, UInt32 len, const void *buf)
99{
100	bypassRobustCommandResponseMatching = true;
101	return handleResponse(nodeID,len,buf);
102}
103
104
105UInt32 IOFireWireAVCCommand::handleResponse(UInt16 nodeID, UInt32 len, const void *buf)
106{
107	FIRELOG_MSG(("IOFireWireAVCCommand::handleResponse (this=0x%08X)\n",this));
108
109    const UInt8 *p;
110    UInt32 i;
111    UInt32 res = kFWResponseAddressError;
112	UInt8 commandAddress;
113	UInt8 commandOpcode;
114
115    // copy the status bytes from fPseudoSpace if this is for us
116    // Don't need to check generation because the command is cancelled when a bus reset happens.
117    // fTimeout is only non-zero if the write was successful.
118    if(fTimeout && nodeID == fWriteNodeID) {
119
120		// Add additional validation, to make sure this AVCResponse packet
121		// seems sensible for matching on our outstanding AVCCommand.
122		// Check the unit/subunit address, and the opcode. Note, If subunit is tape,
123		// and AVCCommand opcode is "Transport State", the AVCResponse packet has the
124		// opcode overwritten with either the Play, Wind, Record, or LoadMedium opcode.
125		if (fCommand)
126		{
127			p = (UInt8*) fCommand;
128			commandAddress = p[kAVCAddress];
129			commandOpcode = p[kAVCOpcode];
130		}
131		else
132		{
133			UInt8 commandBytes[4];
134			IOByteCount cmdByteCount = fMem->readBytes(0,commandBytes,4);
135			if (cmdByteCount != 4)
136				return kFWResponseAddressError;
137			commandAddress = commandBytes[kAVCAddress];
138			commandOpcode = commandBytes[kAVCOpcode];
139		}
140
141        p = (const UInt8 *)buf;
142
143		// Unless bypass flag is set, do the robust command/response matching
144		if (!bypassRobustCommandResponseMatching)
145		{
146			//IOLog("Doing robust response matching for IOFireWireAVCCommand\n");
147
148			if (p[kAVCAddress] != commandAddress)
149				return kFWResponseAddressError;
150
151			if (((commandAddress & 0xF8) == 0x20) && (commandOpcode == 0xD0))
152			{
153				if ( (p[kAVCOpcode] != 0xD0) && (p[kAVCOpcode] != 0xC1) && (p[kAVCOpcode] != 0xC2) && (p[kAVCOpcode] != 0xC3) && (p[kAVCOpcode] != 0xC4))
154					return kFWResponseAddressError;
155			}
156			else
157			{
158				if (p[kAVCOpcode] != commandOpcode)
159					return kFWResponseAddressError;
160			}
161		}
162		else
163		{
164			//IOLog("Bypassing robust response matching for IOFireWireAVCCommand\n");
165		}
166
167		if(p[kAVCCommandResponse] == kAVCInterimStatus) {
168            fTimeout = kInterimTimeout;	// We could wait for ever after the Interim, 10 seconds seems long enough
169            updateTimer();
170        }
171        else {
172            if(len > *fResponseLen)
173                len = *fResponseLen;
174            for (i = 0 ; i < len ; i++)
175                fResponse[i] = *p++;
176            *fResponseLen = len;
177            // Make sure we don't accept another response, we're done!
178            fTimeout = 0;
179            complete(kIOReturnSuccess);
180        }
181        res = kFWResponseComplete;
182    }
183    else {
184        //IOLog("%p: ------ Write not for me ----------\n", this);
185        //IOLog("nodeID: %d-%d\n", nodeID, fWriteNodeID);
186        //IOLog("Data: %x len %d\n", (unsigned int) *(const UInt32 *)buf, (int)len);
187    }
188    return res;
189}
190
191void IOFireWireAVCCommand::free()
192{
193	FIRELOG_MSG(("IOFireWireAVCCommand::free (this=0x%08X)\n",this));
194
195	if(	fIOFireWireAVCCommandExpansion	)
196		fIOFireWireAVCCommandExpansion->fStarted = false;
197
198	if( Busy() )
199	{
200		cancel(kIOReturnAborted);
201	}
202
203    if( fWriteCmd )
204	{
205		if( fWriteCmd->Busy() )
206		{
207			fWriteCmd->cancel(kIOReturnAborted);
208		}
209        fWriteCmd->release();
210		fWriteCmd = NULL;
211    }
212
213    if( fMem )
214	{
215        fMem->release();
216		fMem = NULL;
217	}
218
219	IOFree ( fIOFireWireAVCCommandExpansion, sizeof(ExpansionData) );
220	fIOFireWireAVCCommandExpansion = NULL;
221
222    IOFWCommand::free();
223}
224
225IOReturn IOFireWireAVCCommand::complete(IOReturn state)
226{
227	FIRELOG_MSG(("IOFireWireAVCCommand::complete (this=0x%08X)\n",this));
228
229    if(state == kIOFireWireBusReset && fWriteNodeID == kFWBadNodeID)
230            state = kIOReturnOffline;		// Write was 'retry on bus reset', so device must have gone.
231
232    state = IOFWCommand::complete(state);
233    // If write was sent successfully but response wasn't received, or
234    // Command was written, bus reset happened before response was sent.
235    // - try again
236
237    if( (state == kIOReturnTimeout && fTimeout != 0 && fCurRetries--) ||
238            (state == kIOFireWireBusReset)) {
239        FWAddress addr;
240
241        // setup quad write
242        addr.addressHi   = kCSRRegisterSpaceBaseAddressHi;
243        addr.addressLo   = kFCPCommandAddress;
244        if(fMem)
245            ((IOFWWriteCommand *)fWriteCmd)->reinit(addr, fMem, writeDone, this);
246        else
247            ((IOFWWriteQuadCommand *)fWriteCmd)->reinit(addr,(UInt32 *)fCommand, fCmdLen/4, writeDone, this);
248        fTimeout = 0;
249        return fStatus = startExecution();
250    }
251
252    if( fSync )
253	{
254		if( fSyncWakeup && (not fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled) )
255		{
256			fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = true;
257			fSyncWakeup->signal(state);
258		}
259	}
260    //else if(fComplete)
261	//(*fComplete)(fRefCon, state, fControl, this);
262    return state;
263}
264
265IOReturn IOFireWireAVCCommand::submit(bool queue)
266{
267	fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = false;
268
269	return IOFWCommand::submit(queue);
270}
271
272IOReturn IOFireWireAVCCommand::execute()
273{
274	FIRELOG_MSG(("IOFireWireAVCCommand::execute (this=0x%08X)\n",this));
275
276	if( fIOFireWireAVCCommandExpansion->fStarted == false )
277	{
278		return kIOReturnOffline;
279	}
280
281    fStatus = kIOReturnBusy;
282    fWriteCmd->submit();
283    return fStatus;
284}
285
286IOReturn IOFireWireAVCCommand::resetInterimTimeout()
287{
288	FIRELOG_MSG(("IOFireWireAVCCommand::resetInterimTimeout (this=0x%08X)\n",this));
289
290    // Reinitialize the timeout if we're waiting for the final response after an interim.
291    // Must check internal state with FireWire command gate closed.
292	fControl->closeGate();
293	if(fTimeout == kInterimTimeout && fStatus == kIOReturnBusy)
294        updateTimer();
295	fControl->openGate();
296
297    return kIOReturnSuccess;
298}
299
300bool IOFireWireAVCCommand::init(IOFireWireNub *device, const UInt8 * command, UInt32 cmdLen,
301                                                    UInt8 * response, UInt32 * responseLen)
302{
303	FIRELOG_MSG(("IOFireWireAVCCommand::init (this=0x%08X)\n",this));
304
305    FWAddress addr;
306
307    // setup quad write
308    addr.addressHi   = kCSRRegisterSpaceBaseAddressHi;
309    addr.addressLo   = kFCPCommandAddress;
310
311    IOFWCommand::initWithController(device->getController());
312    // Start out without timeout, update when command is accepted by device
313    fTimeout = 0;
314    fSync = true;
315    fCancelOnReset = true;
316    fResponse = response;
317    fResponseLen = responseLen;
318    fWriteNodeID = kFWBadNodeID;
319    fMaxRetries = 4;
320    fCurRetries = fMaxRetries;
321    fCmdLen = cmdLen;
322	bypassRobustCommandResponseMatching = false;
323
324	// create/clear expansion data
325	fIOFireWireAVCCommandExpansion = (ExpansionData*) IOMalloc( sizeof(ExpansionData) );
326	if( fIOFireWireAVCCommandExpansion == NULL )
327		return false;
328	else
329		bzero( fIOFireWireAVCCommandExpansion, sizeof(ExpansionData) );
330
331    // create command
332    if(cmdLen == 4 || cmdLen == 8) {
333        fMem = NULL;
334        fCommand = command;
335        fWriteCmd = device->createWriteQuadCommand(addr,(UInt32 *)command, cmdLen/4, writeDone, this);
336        if(!fWriteCmd) {
337            return false;
338        }
339    }
340    else {
341        fCommand = NULL;
342        fMem = IOMemoryDescriptor::withAddress((void *)command, cmdLen,
343                    kIODirectionOutIn);
344        if(!fMem) {
345            return false;
346        }
347
348		IOReturn err = fMem->prepare();
349		if( err != kIOReturnSuccess )
350		{
351			fMem->release();
352			return false;
353		}
354
355        fWriteCmd = device->createWriteCommand(addr, fMem, writeDone, this);
356        if(!fWriteCmd) {
357            return false;
358        }
359    }
360
361	fIOFireWireAVCCommandExpansion->fStarted = true;
362	fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = false;
363
364    return true;
365}
366
367IOReturn IOFireWireAVCCommand::reinit(IOFireWireNub *device, const UInt8 * command, UInt32 cmdLen,
368                                                    UInt8 * response, UInt32 * responseLen)
369{
370	FIRELOG_MSG(("IOFireWireAVCCommand::reinit (this=0x%08X)\n",this));
371
372    if(Busy())
373        return fStatus;
374
375    if(fMem) {
376        fMem->release();
377        fMem = NULL;
378    }
379
380    if(fWriteCmd) {
381        fWriteCmd->release();
382        fWriteCmd = NULL;
383    }
384
385    if(init(device, command, cmdLen, response, responseLen))
386        return kIOReturnSuccess;
387    else
388        return kIOReturnNoMemory;
389}
390
391IOFireWireAVCCommand *
392IOFireWireAVCCommand::withNub(IOFireWireNub *device, const UInt8 * command, UInt32 cmdLen,
393                                                    UInt8 * response, UInt32 * responseLen)
394{
395    IOFireWireAVCCommand *me = new IOFireWireAVCCommand;
396
397	FIRELOG_MSG(("IOFireWireAVCCommand::withNub (this=0x%08X)\n",me));
398
399	if(me) {
400        if(!me->init(device, command, cmdLen, response, responseLen)) {
401            me->release();
402            me = NULL;
403        }
404    }
405    return me;
406}
407
408IOFireWireAVCCommand *
409IOFireWireAVCCommand::withNub(IOFireWireNub *device, UInt32 generation,
410            const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 * responseLen)
411{
412
413    IOFireWireAVCCommandInGen *me = new IOFireWireAVCCommandInGen;
414
415	FIRELOG_MSG(("IOFireWireAVCCommand::withNub (this=0x%08X)\n",me));
416
417
418	if(me) {
419        if(!me->init(device, generation, command, cmdLen, response, responseLen)) {
420            me->release();
421            me = NULL;
422        }
423    }
424    return me;
425}
426
427/* --------------- IOFireWireAVCCommandInGen -------------------- */
428OSDefineMetaClassAndStructors(IOFireWireAVCCommandInGen, IOFireWireAVCCommand)
429OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 0);
430OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 1);
431OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 2);
432OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 3);
433
434
435IOReturn IOFireWireAVCCommandInGen::complete(IOReturn state)
436{
437	FIRELOG_MSG(("IOFireWireAVCCommandInGen::complete (this=0x%08X)\n",this));
438
439    state = IOFWCommand::complete(state);
440
441    // If write was sent successfully but response wasn't received - try again
442    // Don't retry on bus reset!
443    if(state == kIOReturnTimeout && fTimeout != 0 && fCurRetries--) {
444        FWAddress addr;
445
446        // setup quad write
447        addr.nodeID		 = fWriteNodeID;
448        addr.addressHi   = kCSRRegisterSpaceBaseAddressHi;
449        addr.addressLo   = kFCPCommandAddress;
450        if(fMem)
451            ((IOFWWriteCommand *)fWriteCmd)->reinit(fWriteGen, addr, fMem, writeDone, this);
452        else
453            ((IOFWWriteQuadCommand *)fWriteCmd)->reinit(fWriteGen, addr,
454                                            (UInt32 *)fCommand, fCmdLen/4, writeDone, this);
455        fTimeout = 0;
456        return fStatus = startExecution();
457    }
458    if(fSync)
459        fSyncWakeup->signal(state);
460    //else if(fComplete)
461	//(*fComplete)(fRefCon, state, fControl, this);
462    return state;
463}
464
465bool IOFireWireAVCCommandInGen::init(IOFireWireNub *device, UInt32 generation,
466            const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 * responseLen)
467{
468	FIRELOG_MSG(("IOFireWireAVCCommandInGen::init (this=0x%08X)\n",this));
469
470    FWAddress addr;
471    UInt32 dummyGen;
472    IOFireWireController *control;
473    // Get nodeID and check generation
474    device->getNodeIDGeneration(dummyGen, fWriteNodeID);
475    fWriteGen = generation;
476
477    // setup quad write
478    addr.nodeID		 = fWriteNodeID;
479    addr.addressHi   = kCSRRegisterSpaceBaseAddressHi;
480    addr.addressLo   = kFCPCommandAddress;
481
482    control = device->getController();
483    IOFWCommand::initWithController(control);
484    // Start out without timeout, update when command is accepted by device
485    fTimeout = 0;
486    fSync = true;
487    fCancelOnReset = true;
488    fResponse = response;
489    fResponseLen = responseLen;
490    fMaxRetries = 4;
491    fCurRetries = fMaxRetries;
492    fCmdLen = cmdLen;
493
494	// create/clear expansion data
495	fIOFireWireAVCCommandExpansion = (ExpansionData*) IOMalloc( sizeof(ExpansionData) );
496	if( fIOFireWireAVCCommandExpansion == NULL )
497		return false;
498	else
499		bzero( fIOFireWireAVCCommandExpansion, sizeof(ExpansionData) );
500
501    // create command
502    if(cmdLen == 4 || cmdLen == 8) {
503        fMem = NULL;
504        fCommand = command;
505        fWriteCmd = new IOFWWriteQuadCommand;
506        if(!fWriteCmd) {
507            return false;
508        }
509        ((IOFWWriteQuadCommand *)fWriteCmd)->initAll(control, generation, addr,
510                                            (UInt32 *)command, cmdLen/4, writeDone, this);
511    }
512    else {
513        fCommand = NULL;
514        fMem = IOMemoryDescriptor::withAddress((void *)command, cmdLen,
515                    kIODirectionOutIn);
516        if(!fMem) {
517            return false;
518        }
519
520		IOReturn err = fMem->prepare();
521		if( err != kIOReturnSuccess )
522		{
523			fMem->release();
524			return false;
525		}
526
527        fWriteCmd = new IOFWWriteCommand;
528        if(!fWriteCmd) {
529            return false;
530        }
531        ((IOFWWriteCommand *)fWriteCmd)->initAll(control, generation, addr, fMem, writeDone, this);
532    }
533
534	fIOFireWireAVCCommandExpansion->fStarted = true;
535	fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = false;
536
537    return true;
538}
539
540IOReturn IOFireWireAVCCommandInGen::reinit(IOFireWireNub *device, UInt32 generation,
541            const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 * responseLen)
542{
543	FIRELOG_MSG(("IOFireWireAVCCommandInGen::reinit (this=0x%08X)\n",this));
544
545    if(Busy())
546        return fStatus;
547    if(fMem) {
548        fMem->release();
549        fMem = NULL;
550    }
551    if(fWriteCmd) {
552        fWriteCmd->release();
553        fWriteCmd = NULL;
554    }
555    if(init(device, generation, command, cmdLen, response, responseLen))
556        return kIOReturnSuccess;
557    else
558        return kIOReturnNoMemory;
559}
560
561