1/*
2 * Copyright (c) 1998-2002 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 * Copyright (c) 1999-2002 Apple Computer, Inc.  All rights reserved.
24 *
25 * HISTORY
26 *
27 *	$Log: IOFWDCLProgram.cpp,v $
28 *	Revision 1.33  2012/06/06 01:51:16  collin
29 *	DCL program changes for vt-d
30 *
31 *	Revision 1.32  2008/05/06 03:26:57  collin
32 *	more K64
33 *
34 *	Revision 1.31  2007/03/14 01:01:12  collin
35 *	*** empty log message ***
36 *
37 *	Revision 1.30  2007/02/15 19:42:07  ayanowit
38 *	For 4369537, eliminated support for legacy DCL SendPacketWithHeader, since it didn't work anyway, and NuDCL does support it.
39 *
40 *	Revision 1.29  2006/02/09 00:21:50  niels
41 *	merge chardonnay branch to tot
42 *
43 *	Revision 1.28  2005/03/12 03:27:51  collin
44 *	*** empty log message ***
45 *
46 *	Revision 1.27  2004/06/19 01:05:50  niels
47 *	turn on prebinding for IOFireWireLib.plugin
48 *
49 *	Revision 1.26  2004/06/10 20:57:36  niels
50 *	*** empty log message ***
51 *
52 *	Revision 1.25  2004/04/22 23:34:11  niels
53 *	*** empty log message ***
54 *
55 *	Revision 1.24  2003/12/19 22:07:46  niels
56 *	send force stop when channel dies/system sleeps
57 *
58 *	Revision 1.23  2003/08/30 00:16:44  collin
59 *	*** empty log message ***
60 *
61 *	Revision 1.22  2003/08/25 09:24:24  niels
62 *	*** empty log message ***
63 *
64 *	Revision 1.20  2003/08/25 08:39:15  niels
65 *	*** empty log message ***
66 *
67 *	Revision 1.19  2003/08/15 04:36:55  niels
68 *	*** empty log message ***
69 *
70 *	Revision 1.18  2003/08/12 00:55:03  niels
71 *	*** empty log message ***
72 *
73 *	Revision 1.17  2003/07/29 23:36:29  niels
74 *	*** empty log message ***
75 *
76 *	Revision 1.16  2003/07/29 22:49:22  niels
77 *	*** empty log message ***
78 *
79 *	Revision 1.15  2003/07/21 06:52:58  niels
80 *	merge isoch to TOT
81 *
82 *	Revision 1.14.4.1  2003/07/01 20:54:06  niels
83 *	isoch merge
84 *
85 *
86 */
87
88#import <IOKit/firewire/IOFWDCLProgram.h>
89#import "FWDebugging.h"
90
91OSDefineMetaClass( IODCLProgram, OSObject )
92OSDefineAbstractStructors ( IODCLProgram, OSObject )
93OSMetaClassDefineReservedUsed ( IODCLProgram, 0 ) ;
94OSMetaClassDefineReservedUsed ( IODCLProgram, 1 ) ;
95OSMetaClassDefineReservedUnused ( IODCLProgram, 2 ) ;
96OSMetaClassDefineReservedUnused ( IODCLProgram, 3 ) ;
97OSMetaClassDefineReservedUnused ( IODCLProgram, 4 ) ;
98
99#undef super
100#define super OSObject
101
102static bool
103getDCLDataBuffer(
104	const DCLCommand *		dcl,
105	IOVirtualRange &		outRange)
106{
107	bool	result = false ;
108
109	switch( dcl->opcode & ~kFWDCLOpFlagMask)
110	{
111		case kDCLSendPacketStartOp:
112		//case kDCLSendPacketWithHeaderStartOp:
113		case kDCLSendPacketOp:
114		case kDCLReceivePacketStartOp:
115		case kDCLReceivePacketOp:
116			outRange.address = (IOVirtualAddress)((DCLTransferPacket*)dcl)->buffer ;
117			outRange.length = ((DCLTransferPacket*)dcl)->size ;
118			result = true ;
119			break ;
120
121		case kDCLPtrTimeStampOp:
122			outRange.address = (IOVirtualAddress)((DCLPtrTimeStamp*)dcl)->timeStampPtr ;
123			outRange.length = sizeof( *( ((DCLPtrTimeStamp*)dcl)->timeStampPtr) ) ;
124			result = true ;
125			break ;
126
127		default:
128			break ;
129	}
130
131	return result ;
132}
133
134void
135IODCLProgram::generateBufferMap( DCLCommand * program )
136{
137
138	IOVirtualAddress lowAddress = (IOVirtualAddress)-1 ;
139	IOVirtualAddress highAddress = 0 ;
140
141	for( DCLCommand * dcl = program; dcl != NULL; dcl = dcl->pNextDCLCommand )
142	{
143		IOVirtualRange tempRange ;
144		if ( getDCLDataBuffer( dcl, tempRange ) )
145		{
146//			DebugLog( "see range %p +0x%x\n", (void*)(tempRange.address), (unsigned)(tempRange.length) ) ;
147
148			lowAddress = MIN( lowAddress, trunc_page( tempRange.address ) ) ;
149			highAddress = MAX( highAddress, round_page( tempRange.address + tempRange.length ) ) ;
150		}
151	}
152
153#ifdef __LP64__
154	DebugLog("IODCLProgram::generateBufferMap lowAddress=%llx highAddress=%llx\n", lowAddress, highAddress ) ;
155#else
156	DebugLog("IODCLProgram::generateBufferMap lowAddress=%x highAddress=%x\n", lowAddress, highAddress ) ;
157#endif
158
159	if ( lowAddress == 0 )
160	{
161		return ;
162	}
163
164    IOByteCount length = highAddress - lowAddress;
165	IOMemoryDescriptor * desc = IOMemoryDescriptor::withAddress( (void*)lowAddress, length, kIODirectionOutIn ) ;
166
167	DebugLogCond(!desc, "couldn't make memory descriptor!\n") ;
168
169	if ( desc && kIOReturnSuccess != desc->prepare() )
170	{
171		ErrorLog("couldn't prepare memory descriptor\n") ;
172
173		desc->release() ;
174		desc = NULL ;
175	}
176
177    IODMACommand * dma_command = NULL;
178    if ( desc )
179	{
180        IOReturn status = kIOReturnSuccess;
181
182        dma_command =  IODMACommand::withSpecification( kIODMACommandOutputHost32,  // segment function
183                                                        32,                         // max address bits
184                                                        length,                     // max segment size
185                                                        (IODMACommand::MappingOptions)(IODMACommand::kMapped | IODMACommand::kIterateOnly), // IO mapped & don't bounce buffer
186                                                        length,                     // max transfer size
187                                                        0,                          // page alignment
188                                                        NULL,                       // mapper
189                                                        NULL );                     // refcon
190        if( dma_command == NULL )
191        {
192            status = kIOReturnError;
193        }
194
195        if( status == kIOReturnSuccess )
196        {
197            status = dma_command->setMemoryDescriptor( desc );
198        }
199
200        if( status != kIOReturnSuccess )
201        {
202            ErrorLog("couldn't prepare memory DMA command\n") ;
203
204            if( dma_command )
205            {
206                dma_command->release();
207            }
208
209            desc->release() ;
210            desc = NULL ;
211        }
212    }
213
214	if ( desc )
215	{
216        fExpansionData->fDMACommand = dma_command;
217		fBufferMem = desc->map() ;
218		desc->release() ;
219
220		DebugLogCond(!fBufferMem, "couldn't make mapping\n") ;
221	}
222}
223
224IOReturn
225IODCLProgram::virtualToPhysical(
226	IOVirtualRange						ranges[],
227	unsigned							rangeCount,
228	IOMemoryCursor::IOPhysicalSegment	outSegments[],
229	unsigned &							outPhysicalSegmentCount,
230	unsigned							maxSegments )
231{
232	// nnn this assumes that subtracting an address of one of the DCL program's buffers in the memory map
233	// from the base address of the map will produce the correct offset into the memory map despite
234	// any gaps in the ranges used to build the memory descriptor
235	// should be okay, since memory descriptors from user space have been allocated from a
236	// single call to vm_allocate()
237
238	outPhysicalSegmentCount = 0 ;
239	if ( rangeCount == 0 )
240		return kIOReturnSuccess ;
241
242	IOVirtualAddress bufferMemBaseAddress = fBufferMem->getVirtualAddress() ;
243
244	unsigned rangeIndex=0;
245	do
246	{
247		if ( outPhysicalSegmentCount >= maxSegments )
248			return kIOReturnDMAError ;
249
250		IOByteCount transferBytes = ranges[ rangeIndex ].length ;
251		IOVirtualAddress offset = ranges[ rangeIndex ].address - bufferMemBaseAddress ;
252
253		while( transferBytes > 0 )
254		{
255			outSegments[ outPhysicalSegmentCount ].location = fBufferMem->getPhysicalSegment( offset, & outSegments[ outPhysicalSegmentCount ].length ) ;
256			outSegments[ outPhysicalSegmentCount ].length = min( outSegments[ outPhysicalSegmentCount ].length, transferBytes ) ;
257
258			transferBytes -= outSegments[ outPhysicalSegmentCount ].length ;
259			offset += outSegments[ outPhysicalSegmentCount ].length ;
260
261			++outPhysicalSegmentCount ;
262		}
263
264	} while ( ++rangeIndex < rangeCount ) ;
265
266	return kIOReturnSuccess ;
267}
268
269bool
270IODCLProgram::init ( IOFireWireBus::DCLTaskInfo * info)
271{
272	if ( ! super::init () )
273		return false ;
274
275	fExpansionData = new ExpansionData ;
276	if ( !fExpansionData )
277	{
278		return false ;
279	}
280
281	fExpansionData->resourceFlags = kFWDefaultIsochResourceFlags ;
282
283	bool success = true ;
284
285	if ( info )
286	{
287		// this part sets up fBufferMem is passed in 'info'
288
289
290		if ( ( !info->unused0 && !info->unused1 && !info->unused2 && !info->unused3 && !info->unused4
291			&& ! info->unused5 ) && info->auxInfo )
292		{
293			switch( info->auxInfo->version )
294			{
295				case 0 :
296				{
297					fBufferMem = info->auxInfo->u.v0.bufferMemoryMap ;
298					if ( fBufferMem )
299					{
300						fBufferMem->retain() ;
301					}
302
303					break ;
304				}
305
306				case 1 :
307				case 2 :
308				{
309					fBufferMem = info->auxInfo->u.v1.bufferMemoryMap ; // handles version 2 also
310					if ( fBufferMem )
311					{
312						fBufferMem->retain() ;
313					}
314
315					break ;
316				}
317				default :
318					ErrorLog( "unsupported version found in info->auxInfo!\n" ) ;
319					success = false ;
320					break ;
321			} ;
322		}
323	}
324
325    return success ;
326}
327
328void IODCLProgram::free()
329{
330	if ( fExpansionData )
331	{
332        if( fExpansionData->fDMACommand )
333        {
334            fExpansionData->fDMACommand->complete();
335            fExpansionData->fDMACommand->release();
336            fExpansionData->fDMACommand = NULL;
337        }
338
339		delete fExpansionData ;
340		fExpansionData = NULL ;
341	}
342
343	if ( fBufferMem )
344	{
345		fBufferMem->release() ;
346		fBufferMem = NULL ;
347	}
348
349    OSObject::free();
350}
351
352IOReturn IODCLProgram::pause()
353{
354    return kIOReturnSuccess;
355}
356
357IOReturn IODCLProgram::resume()
358{
359    return kIOReturnSuccess;
360}
361
362void
363IODCLProgram::setForceStopProc (
364	IOFWIsochChannel::ForceStopNotificationProc proc,
365	void * 						refCon,
366	IOFWIsochChannel *			channel )
367{
368	DebugLog("IODCLProgram::setForceStopProc\n") ;
369}
370
371void
372IODCLProgram::setIsochResourceFlags ( IOFWIsochResourceFlags flags )
373{
374	fExpansionData->resourceFlags = flags ;
375}
376
377IOFWIsochResourceFlags
378IODCLProgram::getIsochResourceFlags () const
379{
380	return fExpansionData->resourceFlags ;
381}
382
383IOMemoryMap *
384IODCLProgram::getBufferMap() const
385{
386	return fBufferMem ;
387}
388