1/* 2 * Copyright (c) 1998-2009 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 * IOFWBufferQ.cpp 24 * IOFireWireFamily 25 * 26 * Created by calderon on 9/17/09. 27 * Copyright 2009 Apple. All rights reserved. 28 * 29 */ 30 31#import "IOFWRingBufferQ.h" 32#import "FWDebugging.h" 33 34// system 35#import <IOKit/IOTypes.h> 36//#import <IOKit/firewire/FireLog.h> 37 38// IOFWRingBufferQ class 39// *** This class is not multithread safe *** 40// Its usage must be lock protected to ensure only one consumer at a time 41 42#define super OSObject 43OSDefineMetaClassAndStructors( IOFWRingBufferQ, OSObject ) ; 44 45//withAddressRange 46// 47 48IOFWRingBufferQ * IOFWRingBufferQ::withAddressRange( mach_vm_address_t address, mach_vm_size_t length, IOOptionBits options, task_t task) 49{ 50 DebugLog("IOFWRingBufferQ::withAddressRange\n"); 51 IOFWRingBufferQ * that = OSTypeAlloc( IOFWRingBufferQ ); 52 53 if ( that ) 54 { 55 if ( that->initQ( address, length, options, task ) ) 56 return that; 57 58 DebugLog("IOFWRingBufferQ::withAddressRange failed initQ\n"); 59 that->release(); 60 } 61 62 return 0; 63} 64 65// initQ 66// inits class specific variables 67 68bool IOFWRingBufferQ::initQ( mach_vm_address_t address, mach_vm_size_t length, IOOptionBits options, task_t task ) 69{ 70 DebugLog("IOFWRingBufferQ::initQ\n"); 71 fMemDescriptor = IOMemoryDescriptor::withAddressRange( address, length, options, task ); 72 if ( !fMemDescriptor ) 73 return false; 74 75 if ( fMemDescriptor->prepare( (IODirection)(options & kIOMemoryDirectionMask) ) != kIOReturnSuccess ) 76 return false; 77 78 fMemDescriptorPrepared = true; 79 fFrontOffset = NULL; 80 fQueueLength = 0; 81 fBufferSize = fMemDescriptor->getLength(); 82 83 return true; 84} 85 86// free 87// 88void IOFWRingBufferQ::free() 89{ 90 if ( fMemDescriptorPrepared ) 91 fMemDescriptor->complete(); 92 93 if ( fMemDescriptor ) 94 fMemDescriptor->release(); 95 96 super::free(); 97} 98 99// isEmpty 100// 101 102bool IOFWRingBufferQ::isEmpty( void ) 103{ 104 return (fQueueLength == 0); 105} 106 107// dequeueBytes 108// Remove the next n bytes in queue given by 'size', but do not return bytes 109 110bool IOFWRingBufferQ::dequeueBytes( IOByteCount size ) 111{ 112 return dequeueBytesWithCopy(NULL, size); 113} 114 115// dequeueBytesWithCopy 116// Remove the next n bytes in queue given by 'size' and write bytes into 'copy' 117 118bool IOFWRingBufferQ::dequeueBytesWithCopy( void * copy, IOByteCount size ) 119{ 120 // zzz is there a guarantee that we will dequeue in first-out order? Maybe it's First-In, Any-Out 121 122 bool success = true; 123 IOByteCount paddingBytes = 0; 124 125 if ( copy ) 126 { 127 // get first entry (of size 'size') in queue 128 if ( front(copy, size, &paddingBytes) ) 129 { 130 fFrontOffset = ( fFrontOffset + paddingBytes + size ) % fBufferSize; // advance front pointer by padding and by this dequeue 131 fQueueLength = fQueueLength - paddingBytes - size; // remove padding and this dequeue size 132 } 133 else 134 success = false; 135 } 136 else 137 { 138 // copy not necessary, simply make bytes available in queue 139 140 IOByteCount theRealFrontOffset = frontEntryOffset( size, &paddingBytes ); // get front offset that corresponds to 'size' 141 fFrontOffset = theRealFrontOffset + size; // advance front pointer by this dequeue 142 fQueueLength = fQueueLength - paddingBytes - size; // remove padding and this dequeue size 143 144 // should have checks to make sure numbers are sensible, if not success=false 145 } 146 147 DebugLog("<<< IOFWRingBufferQ::dequeueBytesWithCopy BSize: %u Length: %u Front: %u Size: %u Copy: %p\n", fBufferSize, fQueueLength, fFrontOffset, size, copy); 148 149 return success; 150} 151 152// getBytes 153// 154 155IOByteCount IOFWRingBufferQ::readBytes(IOByteCount offset, void * bytes, IOByteCount withLength) 156{ 157 return fMemDescriptor->readBytes( offset, bytes, withLength ); 158} 159 160// enqueueBytes 161// Insert 'bytes' into queue contiguously 162 163bool IOFWRingBufferQ::enqueueBytes( void * bytes, IOByteCount size ) 164{ 165 bool success = true; 166 167 IOByteCount offset = 0; 168 IOByteCount paddingBytes = 0; 169 170 // determine if 'bytes' will fit in queue and get the appropriate insertion offset 171 if ( success = willFitAtEnd(size, &offset, &paddingBytes) ) 172 { 173 if ( bytes ) 174 { 175 fQueueLength = fQueueLength + size + paddingBytes; // grow queue appropriately 176 if ( fMemDescriptor->writeBytes(offset, bytes, size) == 0 ) // write 'bytes' into queue at insertion offset 177 success = false; 178 } 179 else 180 { 181 success = false; 182 } 183 } 184 185 DebugLog(">>> IOFWRingBufferQ::enqueueBytes BSize: %u Length: %u Front: %u Insert: %u/%u\n", fBufferSize, fQueueLength, fFrontOffset, offset, paddingBytes); 186 return success; 187} 188 189//isSpaceAvailable 190// 191 192bool IOFWRingBufferQ::isSpaceAvailable( IOByteCount size, IOByteCount * offset ) 193{ 194#if 0 195 FireLog("["); 196 UInt16 i; 197 UInt16 drawBufferWidth = 64; 198 UInt32 drawBufferSize = fBufferSize; 199 UInt32 drawBufAvail = drawBufferWidth * (fBufferSize-fQueueLength) / drawBufferSize; 200 UInt32 drawStartOff = drawBufferWidth * fFrontOffset / drawBufferSize; 201 UInt32 drawDestOff = drawBufferWidth * endOffset / drawBufferSize; 202 203 if ( drawStartOff != 0 ) drawStartOff--; 204 if ( drawDestOff != 0 ) drawDestOff--; 205 206 bool drawFilled = drawStartOff > drawDestOff ? true : false; 207 for ( i=0; i < drawBufferWidth; i++ ) 208 { 209 if ( i == drawStartOff ) 210 FireLog("s"); 211 else if ( i == drawDestOff ) 212 FireLog("d"); 213 else if ( drawFilled ) 214 FireLog("o"); 215 else if ( drawBufAvail <= (drawBufferWidth/2) ) 216 FireLog("?"); 217 else 218 FireLog("."); 219 220 if ( i == drawStartOff ) 221 drawFilled = true; 222 if ( i == drawDestOff ) 223 drawFilled = false; 224 } 225 226 FireLog("]\n"); 227 FireLog("-- Units Available: %lu StartPtr: %lu DestinationPtr: %lu --\n", drawBufAvail, drawStartOff, drawDestOff ); 228#endif 229 230 return willFitAtEnd(size, offset, NULL); 231} 232 233// front 234// Return a copy of first entry, of size 'size' 235 236bool IOFWRingBufferQ::front( void * copy, IOByteCount size, IOByteCount * paddingBytes ) 237{ 238 //FireLog("IOFWRingBufferQ::front\n"); 239 bool success = true; 240 241 if ( isEmpty() ) 242 { 243 success = false; 244 } 245 else 246 { 247 if ( copy ) 248 { 249 IOByteCount frontOffset = frontEntryOffset( size, paddingBytes ); // get proper offset for first entry 250 if ( fMemDescriptor->readBytes(frontOffset, copy, size) == 0 ) 251 success = false; 252 } 253 else 254 { 255 success = false; 256 } 257 } 258 259 return success; 260} 261 262// It's a beautiful day. 263 264// spaceAvailable 265// 266 267IOByteCount IOFWRingBufferQ::spaceAvailable( void ) 268{ 269 return fBufferSize - fQueueLength; 270} 271 272// willFitAtEnd 273// Checks to see if an entry of 'sizeOfEntry' bytes will fit in queue. If so, it will return the offset to which the entry should be written and any padding bytes that should be added to the queue length to compensate for entries that don't fit at the end of the memory range and should be written at beginning of memory range. 274 275bool IOFWRingBufferQ::willFitAtEnd( IOByteCount sizeOfEntry, IOByteCount * offset, IOByteCount * paddingBytes ) 276{ 277 bool success = true; 278 279 if ( paddingBytes ) 280 *paddingBytes = 0; 281 282 IOByteCount endOffset = (fFrontOffset + fQueueLength) % fBufferSize; // pointer that designates end of queue 283 284 if ( fQueueLength < fBufferSize ) // is not full; has space available - avoids empty vs. full confusion 285 { 286 if ( endOffset >= fFrontOffset ) // [__f....e__] 287 { 288 if ( sizeOfEntry > (fBufferSize - endOffset) ) 289 { 290 // cannot fit at end 291 IOByteCount padding = fBufferSize - endOffset; // the number of bytes to get insertion offset to wrap 292 293 if ( paddingBytes ) 294 *paddingBytes = padding; 295 296 endOffset = (fFrontOffset + fQueueLength + padding) % fBufferSize; // advance insertion offset past end of memory range; should be zero 297 298 if ( sizeOfEntry > fFrontOffset ) 299 success = false; // cannot fit at start either 300 } 301 } 302 else // [..e____f..] 303 { 304 if ( sizeOfEntry > (fBufferSize - fQueueLength) ) 305 success = false; // cannot fit in space available 306 } 307 } 308 else if ( fQueueLength > fBufferSize ) 309 { 310 //FireLog("IOFWRingBufferQ::willFitAtEnd queue has grown larger (%u) than buffer size (%u)!\n", fQueueLength, fBufferSize); 311 success = false; 312 } 313 else 314 { 315 // queue is full 316 success = false; 317 } 318 319 if ( offset ) 320 *offset = endOffset; 321 322 DebugLog("IOFWRingBufferQ::willFitAtEnd BSize: %u Length: %u Front: %u Insert: %u EntrySize: %u\n", fBufferSize, fQueueLength, fFrontOffset, endOffset, sizeOfEntry); 323 324 return success; 325} 326 327// frontEntryOffset 328// Get the first entry in the queue. First entry is determined by collecting 'sizeOfEntry' bytes of front of queue. If first entry cannot have been fit between the front queue offset and the actual end of the memory range of the buffer, then it probably exists at the beginning of memory range - paddingBytes represents the number of bytes skipped at the end of memory range to get back to beginning of memory range. 329 330IOByteCount IOFWRingBufferQ::frontEntryOffset( IOByteCount sizeOfEntry, IOByteCount * paddingBytes ) 331{ 332 IOByteCount frontEntryOffset = fFrontOffset; 333 334 IOByteCount endOffset = (fFrontOffset + fQueueLength) % fBufferSize; // pointer that designates end of queue 335 336 if ( paddingBytes ) 337 *paddingBytes = 0; 338 339 if ( !isEmpty() ) // avoid empty vs. full confusion 340 { 341 if ( fFrontOffset >= endOffset ) // [..e____f..] 342 { 343 if ( sizeOfEntry > (fBufferSize - fFrontOffset) ) 344 { 345 // cannot fit at end 346 IOByteCount padding = fBufferSize - fFrontOffset; // the number of bytes to get insertion offset to wrap 347 348 if ( paddingBytes ) 349 *paddingBytes = padding; 350 351 frontEntryOffset = (fFrontOffset + padding) % fBufferSize; // should be zero, since padding made it wrap 352 353 DebugLogCond( (sizeOfEntry > endOffset), "IOFWRingBufferQ::frontEntryOffset Front entry cannot occur within queue starting at buffer index zero!\n"); 354 } 355 // else - normal front offset will work (no padding), do nothing 356 } 357 else // [__f....e__] 358 { 359 DebugLogCond( (sizeOfEntry > fQueueLength), "IOFWRingBufferQ::frontEntryOffset Front entry cannot occur within queue!\n"); 360 } 361 } 362 else 363 { 364 DebugLog("IOFWRingBufferQ::frontEntryOffset Queue is empty!\n"); 365 } 366 367 return frontEntryOffset; 368} 369