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 *  IOFireWireLibPseudoAddressSpace.cpp
24 *  IOFireWireLib
25 *
26 *  Created by NWG on Wed Dec 06 2000.
27 *  Copyright (c) 2000 Apple, Inc. All rights reserved.
28 *
29 */
30
31#import "IOFireWireLibPseudoAddressSpace.h"
32#import "IOFireWireLibDevice.h"
33
34#import <IOKit/iokitmig.h>
35#import <System/libkern/OSCrossEndian.h>
36
37namespace IOFireWireLib {
38
39	PseudoAddressSpace::Interface PseudoAddressSpace::sInterface =
40	{
41		INTERFACEIMP_INTERFACE,
42		1, 1, // version/revision
43		& PseudoAddressSpace::SSetWriteHandler,
44		& PseudoAddressSpace::SSetReadHandler,
45		& PseudoAddressSpace::SSetSkippedPacketHandler,
46		& PseudoAddressSpace::SNotificationIsOn,
47		& PseudoAddressSpace::STurnOnNotification,
48		& PseudoAddressSpace::STurnOffNotification,
49		& PseudoAddressSpace::SClientCommandIsComplete,
50		& PseudoAddressSpace::SGetFWAddress,
51		& PseudoAddressSpace::SGetBuffer,
52		& PseudoAddressSpace::SGetBufferSize,
53		& PseudoAddressSpace::SGetRefCon
54	} ;
55
56	IUnknownVTbl**
57	PseudoAddressSpace::Alloc( Device& userclient, UserObjectHandle inKernAddrSpaceRef, void* inBuffer, UInt32 inBufferSize,
58			void* inBackingStore, void* inRefCon )
59	{
60		PseudoAddressSpace* me = nil ;
61
62		try {
63			me = new PseudoAddressSpace(userclient, inKernAddrSpaceRef, inBuffer, inBufferSize, inBackingStore, inRefCon) ;
64		} catch (...) {
65		}
66
67		return (nil == me) ? nil : reinterpret_cast<IUnknownVTbl**>(& me->GetInterface()) ;
68	}
69
70	HRESULT STDMETHODCALLTYPE
71	PseudoAddressSpace::QueryInterface(REFIID iid, LPVOID* ppv)
72	{
73		HRESULT		result = S_OK ;
74		*ppv = nil ;
75
76		CFUUIDRef	interfaceID	= CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, iid) ;
77
78		if ( CFEqual(interfaceID, IUnknownUUID) ||  CFEqual(interfaceID, kIOFireWirePseudoAddressSpaceInterfaceID) )
79		{
80			*ppv = & GetInterface() ;
81			AddRef() ;
82		}
83		else
84		{
85			*ppv = nil ;
86			result = E_NOINTERFACE ;
87		}
88
89		CFRelease(interfaceID) ;
90		return result ;
91	}
92
93	// ============================================================
94	//
95	// interface table methods
96	//
97	// ============================================================
98
99	const PseudoAddressSpace::WriteHandler
100	PseudoAddressSpace::SSetWriteHandler( AddressSpaceRef self, WriteHandler inWriter )
101	{
102		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->SetWriteHandler(inWriter);
103	}
104
105	const PseudoAddressSpace::ReadHandler
106	PseudoAddressSpace::SSetReadHandler(AddressSpaceRef self, ReadHandler inReader)
107	{
108		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->SetReadHandler(inReader);
109	}
110
111	const PseudoAddressSpace::SkippedPacketHandler
112	PseudoAddressSpace::SSetSkippedPacketHandler(AddressSpaceRef self, SkippedPacketHandler inHandler)
113	{
114		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->SetSkippedPacketHandler(inHandler);
115	}
116
117	Boolean
118	PseudoAddressSpace::SNotificationIsOn(AddressSpaceRef self)
119	{
120		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->mNotifyIsOn;
121	}
122
123	Boolean
124	PseudoAddressSpace::STurnOnNotification(AddressSpaceRef self)
125	{
126		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->TurnOnNotification(self);
127	}
128
129	void
130	PseudoAddressSpace::STurnOffNotification(AddressSpaceRef self)
131	{
132		IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->TurnOffNotification();
133	}
134
135	void
136	PseudoAddressSpace::SClientCommandIsComplete(AddressSpaceRef self, FWClientCommandID commandID, IOReturn status)
137	{
138		IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->ClientCommandIsComplete(commandID, status);
139	}
140
141	void
142	PseudoAddressSpace::SGetFWAddress(AddressSpaceRef self, FWAddress* outAddr)
143	{
144		bcopy (&IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->mFWAddress, outAddr, sizeof(FWAddress));
145	}
146
147	void*
148	PseudoAddressSpace::SGetBuffer(AddressSpaceRef self)
149	{
150		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->GetBuffer() ;
151	}
152
153	const UInt32
154	PseudoAddressSpace::SGetBufferSize(AddressSpaceRef self)
155	{
156		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->mBufferSize;
157	}
158
159	void*
160	PseudoAddressSpace::SGetRefCon(AddressSpaceRef self)
161	{
162		return IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(self)->mRefCon;
163	}
164
165	#pragma mark -
166	// ============================================================
167	//
168	// class methods
169	//
170	// ============================================================
171
172	PseudoAddressSpace::PseudoAddressSpace( Device& userclient, UserObjectHandle inKernAddrSpaceRef,
173												void* inBuffer, UInt32 inBufferSize, void* inBackingStore, void* inRefCon)
174	: IOFireWireIUnknown( reinterpret_cast<const IUnknownVTbl &>( sInterface ) ),
175		mNotifyIsOn(false),
176		mWriter( nil ),
177		mReader( nil ),
178		mSkippedPacketHandler( nil ),
179		mUserClient(userclient),
180		mKernAddrSpaceRef(inKernAddrSpaceRef),
181		mBuffer((char*)inBuffer),
182		mBufferSize(inBufferSize),
183		mBackingStore(inBackingStore),
184		mRefCon(inRefCon)
185	{
186		userclient.AddRef() ;
187
188		mPendingLocks = ::CFDictionaryCreateMutable( kCFAllocatorDefault, 0, NULL, NULL ) ;
189		if (!mPendingLocks)
190			throw kIOReturnNoMemory ;
191
192		AddressSpaceInfo info ;
193
194		IOReturn error ;
195
196		uint32_t outputCnt = 0;
197		size_t outputStructSize =  sizeof( info ) ;
198		const uint64_t inputs[1]={(const uint64_t)mKernAddrSpaceRef};
199
200		error = IOConnectCallMethod(mUserClient.GetUserClientConnection(),
201									kPseudoAddrSpace_GetFWAddrInfo,
202									inputs,1,
203									NULL,0,
204									NULL,&outputCnt,
205									&info,&outputStructSize);
206		if (error)
207		{
208			throw error ;
209		}
210
211#ifndef __LP64__
212		ROSETTA_ONLY(
213			{
214				info.address.nodeID = OSSwapInt16( info.address.nodeID );
215				info.address.addressHi = OSSwapInt16( info.address.addressHi );
216				info.address.addressLo = OSSwapInt32( info.address.addressLo );
217			}
218		);
219#endif
220
221		mFWAddress = info.address ;
222	}
223
224	PseudoAddressSpace::~PseudoAddressSpace()
225	{
226
227		uint32_t outputCnt = 0;
228		const uint64_t inputs[1]={(const uint64_t)mKernAddrSpaceRef};
229
230#if IOFIREWIREUSERCLIENTDEBUG > 0
231		IOReturn error =
232#endif
233
234		IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
235								  kReleaseUserObject,
236								  inputs,1,NULL,&outputCnt);
237
238		DebugLogCond( error, "PseudoAddressSpace::~PseudoAddressSpace: error %x releasing address space!\n", error ) ;
239
240		if( mPendingLocks )
241		{
242			::CFDictionaryRemoveAllValues( mPendingLocks );
243			::CFRelease( mPendingLocks );
244			mPendingLocks = 0;
245		}
246
247		if( mBuffer and mBufferSize > 0 )
248		{
249			delete[] mBuffer;
250			mBuffer		= 0;
251			mBufferSize = 0;
252		}
253
254		mUserClient.Release() ;
255	}
256
257	// callback management
258
259	#pragma mark -
260	#pragma mark --callback management
261
262	const PseudoAddressSpace::WriteHandler
263	PseudoAddressSpace::SetWriteHandler( WriteHandler inWriter )
264	{
265		WriteHandler oldWriter = mWriter ;
266		mWriter = inWriter ;
267
268		return oldWriter ;
269	}
270
271
272	const PseudoAddressSpace::ReadHandler
273	PseudoAddressSpace::SetReadHandler(
274		ReadHandler		inReader)
275	{
276		ReadHandler oldReader = mReader ;
277		mReader = inReader ;
278
279		return oldReader ;
280	}
281
282	const PseudoAddressSpace::SkippedPacketHandler
283	PseudoAddressSpace::SetSkippedPacketHandler(
284		SkippedPacketHandler			inHandler)
285	{
286		SkippedPacketHandler result = mSkippedPacketHandler ;
287		mSkippedPacketHandler = inHandler ;
288
289		return result ;
290	}
291
292	Boolean
293	PseudoAddressSpace::TurnOnNotification( void* callBackRefCon )
294	{
295		IOReturn				err					= kIOReturnSuccess ;
296		io_connect_t			connection			= mUserClient.GetUserClientConnection() ;
297
298		// if notification is already on, skip out.
299		if (mNotifyIsOn)
300			return true ;
301
302		if (!connection)
303			err = kIOReturnNoDevice ;
304
305		if ( kIOReturnSuccess == err )
306		{
307			uint64_t refrncData[kOSAsyncRef64Count];
308			refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) 0;
309			refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)0;
310			const uint64_t inputs[3] = {(const uint64_t)mKernAddrSpaceRef, (const uint64_t)&PseudoAddressSpace::Writer, (const uint64_t)callBackRefCon};
311			uint32_t outputCnt = 0;
312			err = IOConnectCallAsyncScalarMethod(connection,
313												 kSetAsyncRef_Packet,
314												 mUserClient.GetAsyncPort(),
315												 refrncData,kOSAsyncRef64Count,
316												 inputs,3,
317												 NULL,&outputCnt);
318		}
319
320		if ( kIOReturnSuccess == err)
321		{
322			uint64_t refrncData[kOSAsyncRef64Count];
323			refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) 0;
324			refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)0;
325			const uint64_t inputs[3] = {(const uint64_t)mKernAddrSpaceRef, (const uint64_t)& SkippedPacket, (const uint64_t)callBackRefCon};
326			uint32_t outputCnt = 0;
327			err = IOConnectCallAsyncScalarMethod(connection,
328												 kSetAsyncRef_SkippedPacket,
329												 mUserClient.GetAsyncPort(),
330												 refrncData,kOSAsyncRef64Count,
331												 inputs,3,
332												 NULL,&outputCnt);
333		}
334
335		if ( kIOReturnSuccess == err)
336		{
337			uint64_t refrncData[kOSAsyncRef64Count];
338			refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) 0;
339			refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)0;
340			const uint64_t inputs[3] = {(const uint64_t)mKernAddrSpaceRef, (const uint64_t)& Reader, (const uint64_t)callBackRefCon};
341			uint32_t outputCnt = 0;
342			err = IOConnectCallAsyncScalarMethod(connection,
343												 kSetAsyncRef_Read,
344												 mUserClient.GetAsyncPort(),
345												 refrncData,kOSAsyncRef64Count,
346												 inputs,3,
347												 NULL,&outputCnt);
348		}
349
350		if ( kIOReturnSuccess == err )
351			mNotifyIsOn = true ;
352
353		return ( kIOReturnSuccess == err ) ;
354	}
355
356	void
357	PseudoAddressSpace::TurnOffNotification()
358	{
359		IOReturn				err					= kIOReturnSuccess ;
360		io_connect_t			connection			= mUserClient.GetUserClientConnection() ;
361
362		// if notification isn't on, skip out.
363		if (!mNotifyIsOn)
364			return ;
365
366		if (!connection)
367			err = kIOReturnNoDevice ;
368
369		if ( kIOReturnSuccess == err )
370		{
371
372			uint64_t refrncData[kOSAsyncRef64Count];
373			refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) 0;
374			refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)0;
375			const uint64_t inputs[3] = {(const uint64_t)mKernAddrSpaceRef, (const uint64_t)0, (const uint64_t)this};
376			uint32_t outputCnt = 0;
377			err = IOConnectCallAsyncScalarMethod(connection,
378												 kSetAsyncRef_Packet,
379												 mUserClient.GetAsyncPort(),
380												 refrncData,kOSAsyncRef64Count,
381												 inputs,3,
382												 NULL,&outputCnt);
383
384			outputCnt = 0;
385			err = IOConnectCallAsyncScalarMethod(connection,
386												 kSetAsyncRef_SkippedPacket,
387												 mUserClient.GetAsyncPort(),
388												 refrncData,kOSAsyncRef64Count,
389												 inputs,3,
390												 NULL,&outputCnt);
391
392			outputCnt = 0;
393			err = IOConnectCallAsyncScalarMethod(connection,
394												 kSetAsyncRef_Read,
395												 mUserClient.GetAsyncPort(),
396												 refrncData,kOSAsyncRef64Count,
397												 inputs,3,
398												 NULL,&outputCnt);
399		}
400
401		mNotifyIsOn = false ;
402	}
403
404	void
405	PseudoAddressSpace::ClientCommandIsComplete(
406		FWClientCommandID				commandID,
407		IOReturn						status)
408	{
409		void**		args ;
410
411		if (::CFDictionaryGetValueIfPresent( mPendingLocks, commandID, (const void**) &args ) && (status == kIOReturnSuccess) )
412		{
413			::CFDictionaryRemoveValue( mPendingLocks, commandID ) ;
414			AddressSpaceRef 	addressSpaceRef = (AddressSpaceRef) args[0] ;
415
416			++args ;	// we tacked on an extra arg at the beginning, so we undo that.
417
418			bool	equal ;
419			UInt32	offset = (unsigned long)args[6] ;
420
421			if ( (unsigned long) args[1] == 8 )
422				// 32-bit compare
423				equal = *(UInt32*)((char*)mBackingStore + offset) == *(UInt32*)(mBuffer + (unsigned long)args[2]) ;
424			else
425				// 64-bit compare
426				equal = *(UInt64*)((char*)mBackingStore + offset) == *(UInt64*)(mBuffer + (unsigned long)args[2]) ;
427
428			if ( equal )
429			{
430				mWriter(
431					addressSpaceRef,
432					(FWClientCommandID)(args[0]),						// commandID,
433					(unsigned long)(args[1]) >> 1,								// packetSize
434					mBuffer + (unsigned long)args[2] + ( (unsigned long) args[1] == 8 ? 4 : 8),// packet
435					(UInt16)(unsigned long)args[3],							// nodeID
436					(unsigned long)(args[5]),									// addr.nodeID, addr.addressHi,
437					(unsigned long)(args[6]),
438					(void*) mRefCon) ;									// refcon
439			}
440			else
441				status = kFWResponseAddressError ;
442
443			delete[] (args-1) ;
444		}
445
446		uint32_t outputCnt = 0;
447		const uint64_t inputs[3] = {(const uint64_t)mKernAddrSpaceRef, (const uint64_t)commandID, status};
448
449		#if IOFIREWIREUSERCLIENTDEBUG > 0
450		OSStatus err =
451		#endif
452
453		IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
454								  kPseudoAddrSpace_ClientCommandIsComplete,
455								  inputs,3,
456								  NULL,&outputCnt);
457
458#ifdef __LP64__
459		DebugLogCond( err, "PseudoAddressSpace::ClientCommandIsComplete: err=0x%08X\n", (UInt32)err ) ;
460#else
461		DebugLogCond( err, "PseudoAddressSpace::ClientCommandIsComplete: err=0x%08lX\n", (UInt32)err ) ;
462#endif
463	}
464
465	void
466	PseudoAddressSpace::Writer( AddressSpaceRef refcon, IOReturn result, void** args, int numArgs)
467	{
468		PseudoAddressSpace* me = IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(refcon) ;
469
470		if ( !me->mWriter || ( (bool)args[7] && !me->mReader) )
471		{
472			me->ClientCommandIsComplete( args[0], kFWResponseTypeError) ;
473			return ;
474		}
475		else if ( (bool)args[7] )
476		{
477//			void** lockValues 	= new (void*)[numArgs+1] ;
478			void** lockValues 	= (void**) new UInt32 *[numArgs+1] ;
479
480			bcopy( args, & lockValues[1], sizeof(void*) * numArgs ) ;
481			lockValues[0] = refcon ;
482
483			::CFDictionaryAddValue( me->mPendingLocks, args[0], lockValues ) ;
484
485			UInt32 offset = (unsigned long)args[6] ;	// !!! hack - all address spaces have 0 for addressLo
486
487			(me->mReader)( (AddressSpaceRef) refcon,
488							(FWClientCommandID)(args[0]),					// commandID,
489							(unsigned long)(args[1]),								// packetSize
490							offset,											// packetOffset
491							(UInt16)(unsigned long)(args[3]),						// nodeID; double cast avoids compiler warning
492							(unsigned long)(args[5]),								// addr.addressHi,
493							(unsigned long)(args[6]),								// addr.addressLo
494							(void*) me->mRefCon) ;							// refcon
495
496		}
497		else
498		{
499			(me->mWriter)(
500				(AddressSpaceRef) refcon,
501				(FWClientCommandID) args[0],						// commandID,
502				(unsigned long)(args[1]),									// packetSize
503				me->mBuffer + (unsigned long)(args[2]),					// packet
504				(UInt16)(unsigned long)(args[3]),							// nodeID
505				(unsigned long)(args[5]),									// addr.addressHi, addr.addressLo
506				(unsigned long)(args[6]),
507				(void*) me->mRefCon) ;								// refcon
508
509		}
510	}
511
512	void
513	PseudoAddressSpace::SkippedPacket( AddressSpaceRef refcon, IOReturn result, FWClientCommandID commandID, UInt32 packetCount)
514	{
515		PseudoAddressSpace* me = IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(refcon) ;
516
517		if (me->mSkippedPacketHandler)
518			(me->mSkippedPacketHandler)( refcon, commandID, packetCount) ;
519	}
520
521	void
522	PseudoAddressSpace::Reader( AddressSpaceRef	refcon, IOReturn result, void** args, int numArgs )
523	{
524		PseudoAddressSpace* me = IOFireWireIUnknown::InterfaceMap<PseudoAddressSpace>::GetThis(refcon) ;
525
526
527		if (me->mReader)
528		{
529			(me->mReader)( (AddressSpaceRef) refcon,
530						(FWClientCommandID) args[0],					// commandID,
531						(unsigned long)(args[1]),								// packetSize
532						(unsigned long)(args[2]),								// packetOffset
533						(UInt16)(unsigned long)(args[3]),						// nodeID
534						(unsigned long)(args[5]),								// addr.nodeID, addr.addressHi,
535						(unsigned long)(args[6]),
536						(void*) me->mRefCon) ;							// refcon
537		}
538		else
539			me->ClientCommandIsComplete( args[0], //commandID
540									kFWResponseTypeError) ;
541	}
542
543
544	#pragma mark -
545	#pragma mark --accessors
546
547	const FWAddress&
548	PseudoAddressSpace::GetFWAddress()
549	{
550		return mFWAddress ;
551	}
552
553	void*
554	PseudoAddressSpace::GetBuffer()
555	{
556		return mBackingStore ;	// I assume this is what the user wants instead of
557								// the queue buffer stored in mBuffer.
558	}
559
560	const UInt32
561	PseudoAddressSpace::GetBufferSize()
562	{
563		return mBufferSize ;
564	}
565
566	void*
567	PseudoAddressSpace::GetRefCon()
568	{
569		return mRefCon ;
570	}
571}
572