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