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
33 OSDefineMetaClassAndStructors(IOMultiMemoryDescriptor, IOMemoryDescriptor)
34 
35 IOMultiMemoryDescriptor * 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 
64 bool 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     assert(withCount);
80 
81     // Release existing descriptors, if any
82     if ( _descriptors )
83     {
84         for ( unsigned index = 0; index < _descriptorsCount; index++ )
85             _descriptors[index]->release();
86 
87         if ( _descriptorsIsAllocated )
88             IODelete(_descriptors, IOMemoryDescriptor *, _descriptorsCount);
89     } else {
90         // Ask our superclass' opinion.
91         if ( super::init() == false )  return false;
92     }
93 
94     // Initialize our minimal state.
95 
96     _descriptors            = 0;
97     _descriptorsCount       = withCount;
98     _descriptorsIsAllocated = asReference ? false : true;
99     _flags                  = withDirection;
100 #ifndef __LP64__
101     _direction              = (IODirection) (_flags & kIOMemoryDirectionMask);
102 #endif /* !__LP64__ */
103     _length                 = 0;
104     _mappings               = 0;
105     _tag                    = 0;
106 
107     if ( asReference )
108     {
109         _descriptors = descriptors;
110     }
111     else
112     {
113         _descriptors = IONew(IOMemoryDescriptor *, withCount);
114         if ( _descriptors == 0 )  return false;
115 
116         bcopy( /* from  */ descriptors,
117                /* to    */ _descriptors,
118                /* bytes */ withCount * sizeof(IOMemoryDescriptor *) );
119     }
120 
121     for ( unsigned index = 0; index < withCount; index++ )
122     {
123         descriptors[index]->retain();
124         _length += descriptors[index]->getLength();
125         if ( _tag == 0 )  _tag = descriptors[index]->getTag();
126         assert(descriptors[index]->getDirection() == withDirection);
127     }
128 
129     return true;
130 }
131 
132 void 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 
150 IOReturn 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 
187 IOReturn 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 
215 addr64_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