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/IOMultiMemoryDescriptor.h>
31
32#define super IOMemoryDescriptor
33OSDefineMetaClassAndStructors(IOMultiMemoryDescriptor, IOMemoryDescriptor)
34
35IOMultiMemoryDescriptor * IOMultiMemoryDescriptor::withDescriptors(
36                                  IOMemoryDescriptor ** descriptors,
37                                  UInt32                withCount,
38                                  IODirection           withDirection,
39                                  bool                  asReference )
40{
41    //
42    // Create a new IOMultiMemoryDescriptor.  The "buffer" is made up of several
43    // memory descriptors, that are to be chained end-to-end to make up a single
44    // memory descriptor.
45    //
46    // Passing the ranges as a reference will avoid an extra allocation.
47    //
48
49    IOMultiMemoryDescriptor * me = new IOMultiMemoryDescriptor;
50
51    if ( me && me->initWithDescriptors(
52                                  /* descriptors   */ descriptors,
53                                  /* withCount     */ withCount,
54                                  /* withDirection */ withDirection,
55                                  /* asReference   */ asReference ) == false )
56    {
57	    me->release();
58	    me = 0;
59    }
60
61    return me;
62}
63
64bool IOMultiMemoryDescriptor::initWithDescriptors(
65                                  IOMemoryDescriptor ** descriptors,
66                                  UInt32                withCount,
67                                  IODirection           withDirection,
68                                  bool                  asReference )
69{
70    //
71    // Initialize an IOMultiMemoryDescriptor. The "buffer" is made up of several
72    // memory descriptors, that are to be chained end-to-end to make up a single
73    // memory descriptor.
74    //
75    // Passing the ranges as a reference will avoid an extra allocation.
76    //
77
78    assert(descriptors);
79
80    // Release existing descriptors, if any
81    if ( _descriptors )
82    {
83        for ( unsigned index = 0; index < _descriptorsCount; index++ )
84            _descriptors[index]->release();
85
86        if ( _descriptorsIsAllocated )
87            IODelete(_descriptors, IOMemoryDescriptor *, _descriptorsCount);
88    } else {
89        // Ask our superclass' opinion.
90        if ( super::init() == false )  return false;
91    }
92
93    // Initialize our minimal state.
94
95    _descriptors            = 0;
96    _descriptorsCount       = withCount;
97    _descriptorsIsAllocated = asReference ? false : true;
98    _flags                  = withDirection;
99#ifndef __LP64__
100    _direction              = (IODirection) (_flags & kIOMemoryDirectionMask);
101#endif /* !__LP64__ */
102    _length                 = 0;
103    _mappings               = 0;
104    _tag                    = 0;
105
106    if ( asReference )
107    {
108        _descriptors = descriptors;
109    }
110    else
111    {
112        _descriptors = IONew(IOMemoryDescriptor *, withCount);
113        if ( _descriptors == 0 )  return false;
114
115        bcopy( /* from  */ descriptors,
116               /* to    */ _descriptors,
117               /* bytes */ withCount * sizeof(IOMemoryDescriptor *) );
118    }
119
120    for ( unsigned index = 0; index < withCount; index++ )
121    {
122        descriptors[index]->retain();
123        _length += descriptors[index]->getLength();
124        if ( _tag == 0 )  _tag = descriptors[index]->getTag();
125        assert(descriptors[index]->getDirection() ==
126	       (withDirection & kIOMemoryDirectionMask));
127    }
128
129    return true;
130}
131
132void IOMultiMemoryDescriptor::free()
133{
134    //
135    // Free all of this object's outstanding resources.
136    //
137
138    if ( _descriptors )
139    {
140        for ( unsigned index = 0; index < _descriptorsCount; index++ )
141            _descriptors[index]->release();
142
143        if ( _descriptorsIsAllocated )
144            IODelete(_descriptors, IOMemoryDescriptor *, _descriptorsCount);
145    }
146
147    super::free();
148}
149
150IOReturn IOMultiMemoryDescriptor::prepare(IODirection forDirection)
151{
152    //
153    // Prepare the memory for an I/O transfer.
154    //
155    // This involves paging in the memory and wiring it down for the duration
156    // of the transfer.  The complete() method finishes the processing of the
157    // memory after the I/O transfer finishes.
158    //
159
160    unsigned index;
161    IOReturn status = kIOReturnInternalError;
162    IOReturn statusUndo;
163
164    if ( forDirection == kIODirectionNone )
165    {
166        forDirection = getDirection();
167    }
168
169    for ( index = 0; index < _descriptorsCount; index++ )
170    {
171        status = _descriptors[index]->prepare(forDirection);
172        if ( status != kIOReturnSuccess )  break;
173    }
174
175    if ( status != kIOReturnSuccess )
176    {
177        for ( unsigned indexUndo = 0; indexUndo <= index; indexUndo++ )
178        {
179            statusUndo = _descriptors[index]->complete(forDirection);
180            assert(statusUndo == kIOReturnSuccess);
181        }
182    }
183
184    return status;
185}
186
187IOReturn IOMultiMemoryDescriptor::complete(IODirection forDirection)
188{
189    //
190    // Complete processing of the memory after an I/O transfer finishes.
191    //
192    // This method shouldn't be called unless a prepare() was previously issued;
193    // the prepare() and complete() must occur in pairs, before and after an I/O
194    // transfer.
195    //
196
197    IOReturn status;
198    IOReturn statusFinal = kIOReturnSuccess;
199
200    if ( forDirection == kIODirectionNone )
201    {
202        forDirection = getDirection();
203    }
204
205    for ( unsigned index = 0; index < _descriptorsCount; index++ )
206    {
207        status = _descriptors[index]->complete(forDirection);
208        if ( status != kIOReturnSuccess )  statusFinal = status;
209        assert(status == kIOReturnSuccess);
210    }
211
212    return statusFinal;
213}
214
215addr64_t IOMultiMemoryDescriptor::getPhysicalSegment(
216                                                       IOByteCount   offset,
217                                                       IOByteCount * length,
218                                                       IOOptionBits  options )
219{
220    //
221    // This method returns the physical address of the byte at the given offset
222    // into the memory,  and optionally the length of the physically contiguous
223    // segment from that offset.
224    //
225
226    assert(offset <= _length);
227
228    for ( unsigned index = 0; index < _descriptorsCount; index++ )
229    {
230        if ( offset < _descriptors[index]->getLength() )
231        {
232            return _descriptors[index]->getPhysicalSegment(offset, length, options);
233        }
234        offset -= _descriptors[index]->getLength();
235    }
236
237    if ( length )  *length = 0;
238
239    return 0;
240}
241