1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <IOKit/IOLib.h>
30#include <IOKit/IOInterleavedMemoryDescriptor.h>
31
32#define super IOMemoryDescriptor
33OSDefineMetaClassAndStructors(IOInterleavedMemoryDescriptor, IOMemoryDescriptor)
34
35IOInterleavedMemoryDescriptor * IOInterleavedMemoryDescriptor::withCapacity(
36                                  IOByteCount           capacity,
37                                  IODirection           direction )
38{
39    //
40    // Create a new IOInterleavedMemoryDescriptor.  The "buffer" will be made up
41    // of several memory descriptors, that are to be chained end-to-end to make up
42    // a single memory descriptor.
43    //
44
45    IOInterleavedMemoryDescriptor * me = new IOInterleavedMemoryDescriptor;
46
47    if ( me && !me->initWithCapacity(
48                                  /* capacity  */ capacity,
49                                  /* direction */ direction ))
50    {
51	    me->release();
52	    me = 0;
53    }
54
55    return me;
56}
57
58bool IOInterleavedMemoryDescriptor::initWithCapacity(
59                                  IOByteCount           capacity,
60                                  IODirection           direction )
61{
62    //
63    // Initialize an IOInterleavedMemoryDescriptor. The "buffer" will be made up
64    // of several memory descriptors, that are to be chained end-to-end to make up
65    // a single memory descriptor.
66    //
67
68    assert(capacity);
69
70    // Ask our superclass' opinion.
71    if ( super::init() == false )  return false;
72
73    // Initialize our minimal state.
74
75    _flags                  = direction;
76#ifndef __LP64__
77    _direction              = (IODirection) (_flags & kIOMemoryDirectionMask);
78#endif /* !__LP64__ */
79    _length                 = 0;
80    _mappings               = 0;
81    _tag                    = 0;
82    _descriptorCount        = 0;
83    _descriptors            = IONew(IOMemoryDescriptor *, capacity);
84    _descriptorOffsets      = IONew(IOByteCount, capacity);
85    _descriptorLengths      = IONew(IOByteCount, capacity);
86
87    if ( (_descriptors == 0) || (_descriptorOffsets == 0) || (_descriptorLengths == 0) )
88        return false;
89
90    _descriptorCapacity     = capacity;
91
92    return true;
93}
94
95void IOInterleavedMemoryDescriptor::clearMemoryDescriptors( IODirection direction )
96{
97    UInt32 index;
98
99    for ( index = 0; index < _descriptorCount; index++ )
100    {
101        if ( _descriptorPrepared )
102	    _descriptors[index]->complete(getDirection());
103
104	_descriptors[index]->release();
105	_descriptors[index] = 0;
106
107	_descriptorOffsets[index] = 0;
108	_descriptorLengths[index] = 0;
109    }
110
111    if ( direction != kIODirectionNone )
112    {
113        _flags = (_flags & ~kIOMemoryDirectionMask) | direction;
114#ifndef __LP64__
115        _direction = (IODirection) (_flags & kIOMemoryDirectionMask);
116#endif /* !__LP64__ */
117    }
118
119    _descriptorCount = 0;
120    _length = 0;
121    _mappings = 0;
122    _tag = 0;
123
124};
125
126bool IOInterleavedMemoryDescriptor::setMemoryDescriptor(
127                                             IOMemoryDescriptor * descriptor,
128					     IOByteCount offset,
129					     IOByteCount length )
130{
131    if ( _descriptorPrepared || (_descriptorCount == _descriptorCapacity) )
132        return false;
133
134    if ( (offset + length) > descriptor->getLength() )
135        return false;
136
137//    if ( descriptor->getDirection() != getDirection() )
138//        return false;
139
140    descriptor->retain();
141    _descriptors[_descriptorCount] = descriptor;
142    _descriptorOffsets[_descriptorCount] = offset;
143    _descriptorLengths[_descriptorCount] = length;
144
145    _descriptorCount++;
146
147    _length += length;
148
149    return true;
150}
151
152void IOInterleavedMemoryDescriptor::free()
153{
154    //
155    // Free all of this object's outstanding resources.
156    //
157
158    if ( _descriptors )
159    {
160        for ( unsigned index = 0; index < _descriptorCount; index++ )
161            _descriptors[index]->release();
162
163        if ( _descriptors != 0 )
164            IODelete(_descriptors, IOMemoryDescriptor *, _descriptorCapacity);
165
166        if ( _descriptorOffsets != 0 )
167            IODelete(_descriptorOffsets, IOMemoryDescriptor *, _descriptorCapacity);
168
169        if ( _descriptorLengths != 0 )
170            IODelete(_descriptorLengths, IOMemoryDescriptor *, _descriptorCapacity);
171    }
172
173    super::free();
174}
175
176IOReturn IOInterleavedMemoryDescriptor::prepare(IODirection forDirection)
177{
178    //
179    // Prepare the memory for an I/O transfer.
180    //
181    // This involves paging in the memory and wiring it down for the duration
182    // of the transfer.  The complete() method finishes the processing of the
183    // memory after the I/O transfer finishes.
184    //
185
186    unsigned index;
187    IOReturn status = kIOReturnSuccess;
188    IOReturn statusUndo;
189
190    if ( forDirection == kIODirectionNone )
191    {
192        forDirection = getDirection();
193    }
194
195    for ( index = 0; index < _descriptorCount; index++ )
196    {
197        status = _descriptors[index]->prepare(forDirection);
198        if ( status != kIOReturnSuccess )  break;
199    }
200
201    if ( status != kIOReturnSuccess )
202    {
203        for ( unsigned indexUndo = 0; indexUndo < index; indexUndo++ )
204        {
205            statusUndo = _descriptors[index]->complete(forDirection);
206            assert(statusUndo == kIOReturnSuccess);
207        }
208    }
209
210    if ( status == kIOReturnSuccess ) _descriptorPrepared = true;
211
212    return status;
213}
214
215IOReturn IOInterleavedMemoryDescriptor::complete(IODirection forDirection)
216{
217    //
218    // Complete processing of the memory after an I/O transfer finishes.
219    //
220    // This method shouldn't be called unless a prepare() was previously issued;
221    // the prepare() and complete() must occur in pairs, before and after an I/O
222    // transfer.
223    //
224
225    IOReturn status;
226    IOReturn statusFinal = kIOReturnSuccess;
227
228    if ( forDirection == kIODirectionNone )
229    {
230        forDirection = getDirection();
231    }
232
233    for ( unsigned index = 0; index < _descriptorCount; index++ )
234    {
235        status = _descriptors[index]->complete(forDirection);
236        if ( status != kIOReturnSuccess )  statusFinal = status;
237        assert(status == kIOReturnSuccess);
238    }
239
240    _descriptorPrepared = false;
241
242    return statusFinal;
243}
244
245addr64_t IOInterleavedMemoryDescriptor::getPhysicalSegment(
246                                                       IOByteCount   offset,
247                                                       IOByteCount * length,
248                                                       IOOptionBits  options )
249{
250    //
251    // This method returns the physical address of the byte at the given offset
252    // into the memory,  and optionally the length of the physically contiguous
253    // segment from that offset.
254    //
255
256    addr64_t pa;
257
258    assert(offset <= _length);
259
260    for ( unsigned index = 0; index < _descriptorCount; index++ )
261    {
262        if ( offset < _descriptorLengths[index] )
263        {
264            pa = _descriptors[index]->getPhysicalSegment(_descriptorOffsets[index] + offset, length, options);
265	    if ((_descriptorLengths[index] - offset) < *length) *length = _descriptorLengths[index] - offset;
266            return pa;
267        }
268        offset -= _descriptorLengths[index];
269    }
270
271    if ( length )  *length = 0;
272
273    return 0;
274}
275