2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <IOKit/IOSharedDataQueue.h>
30 #include <IOKit/IODataQueueShared.h>
31 #include <IOKit/IOLib.h>
32 #include <IOKit/IOMemoryDescriptor.h>
42 #define super IODataQueue
44 OSDefineMetaClassAndStructors(IOSharedDataQueue
, IODataQueue
)
46 IOSharedDataQueue
*IOSharedDataQueue::withCapacity(UInt32 size
)
48 IOSharedDataQueue
*dataQueue
= new IOSharedDataQueue
;
51 if (!dataQueue
->initWithCapacity(size
)) {
60 IOSharedDataQueue
*IOSharedDataQueue::withEntries(UInt32 numEntries
, UInt32 entrySize
)
62 IOSharedDataQueue
*dataQueue
= new IOSharedDataQueue
;
65 if (!dataQueue
->initWithEntries(numEntries
, entrySize
)) {
74 Boolean
IOSharedDataQueue::initWithCapacity(UInt32 size
)
76 IODataQueueAppendix
* appendix
;
83 _reserved
= (ExpansionData
*)IOMalloc(sizeof(struct ExpansionData
));
88 if (size
> UINT32_MAX
- DATA_QUEUE_MEMORY_HEADER_SIZE
- DATA_QUEUE_MEMORY_APPENDIX_SIZE
) {
92 allocSize
= round_page(size
+ DATA_QUEUE_MEMORY_HEADER_SIZE
+ DATA_QUEUE_MEMORY_APPENDIX_SIZE
);
94 if (allocSize
< size
) {
98 dataQueue
= (IODataQueueMemory
*)IOMallocAligned(allocSize
, PAGE_SIZE
);
102 bzero(dataQueue
, allocSize
);
104 dataQueue
->queueSize
= size
;
105 // dataQueue->head = 0;
106 // dataQueue->tail = 0;
108 if (!setQueueSize(size
)) {
112 appendix
= (IODataQueueAppendix
*)((UInt8
*)dataQueue
+ size
+ DATA_QUEUE_MEMORY_HEADER_SIZE
);
113 appendix
->version
= 0;
116 notifyMsg
= IOMalloc(sizeof(mach_msg_header_t
));
120 bzero(notifyMsg
, sizeof(mach_msg_header_t
));
122 setNotificationPort(MACH_PORT_NULL
);
127 void IOSharedDataQueue::free()
130 IOFreeAligned(dataQueue
, round_page(getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE
+ DATA_QUEUE_MEMORY_APPENDIX_SIZE
));
133 IOFree(notifyMsg
, sizeof(mach_msg_header_t
));
139 IOFree (_reserved
, sizeof(struct ExpansionData
));
146 IOMemoryDescriptor
*IOSharedDataQueue::getMemoryDescriptor()
148 IOMemoryDescriptor
*descriptor
= 0;
150 if (dataQueue
!= 0) {
151 descriptor
= IOMemoryDescriptor::withAddress(dataQueue
, getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE
+ DATA_QUEUE_MEMORY_APPENDIX_SIZE
, kIODirectionOutIn
);
158 IODataQueueEntry
* IOSharedDataQueue::peek()
160 IODataQueueEntry
*entry
= 0;
162 if (dataQueue
&& (dataQueue
->head
!= dataQueue
->tail
)) {
163 IODataQueueEntry
* head
= 0;
165 UInt32 headOffset
= dataQueue
->head
;
166 UInt32 queueSize
= getQueueSize();
168 if (headOffset
>= queueSize
) {
172 head
= (IODataQueueEntry
*)((char *)dataQueue
->queue
+ headOffset
);
173 headSize
= head
->size
;
175 // Check if there's enough room before the end of the queue for a header.
176 // If there is room, check if there's enough room to hold the header and
179 if ((headOffset
> UINT32_MAX
- DATA_QUEUE_ENTRY_HEADER_SIZE
) ||
180 (headOffset
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> queueSize
) ||
181 (headOffset
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> UINT32_MAX
- headSize
) ||
182 (headOffset
+ headSize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> queueSize
)) {
183 // No room for the header or the data, wrap to the beginning of the queue.
184 // Note: wrapping even with the UINT32_MAX checks, as we have to support
185 // queueSize of UINT32_MAX
186 entry
= dataQueue
->queue
;
195 Boolean
IOSharedDataQueue::enqueue(void * data
, UInt32 dataSize
)
197 const UInt32 head
= dataQueue
->head
; // volatile
198 const UInt32 tail
= dataQueue
->tail
;
199 const UInt32 entrySize
= dataSize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
;
200 IODataQueueEntry
* entry
;
202 // Check for overflow of entrySize
203 if (dataSize
> UINT32_MAX
- DATA_QUEUE_ENTRY_HEADER_SIZE
) {
206 // Check for underflow of (getQueueSize() - tail)
207 if (getQueueSize() < tail
|| getQueueSize() < head
) {
213 // Is there enough room at the end for the entry?
214 if ((entrySize
<= UINT32_MAX
- tail
) &&
215 ((tail
+ entrySize
) <= getQueueSize()) )
217 entry
= (IODataQueueEntry
*)((UInt8
*)dataQueue
->queue
+ tail
);
219 entry
->size
= dataSize
;
220 memcpy(&entry
->data
, data
, dataSize
);
222 // The tail can be out of bound when the size of the new entry
223 // exactly matches the available space at the end of the queue.
224 // The tail can range from 0 to dataQueue->queueSize inclusive.
226 OSAddAtomic(entrySize
, (SInt32
*)&dataQueue
->tail
);
228 else if ( head
> entrySize
) // Is there enough room at the beginning?
230 // Wrap around to the beginning, but do not allow the tail to catch
233 dataQueue
->queue
->size
= dataSize
;
235 // We need to make sure that there is enough room to set the size before
236 // doing this. The user client checks for this and will look for the size
237 // at the beginning if there isn't room for it at the end.
239 if ( ( getQueueSize() - tail
) >= DATA_QUEUE_ENTRY_HEADER_SIZE
)
241 ((IODataQueueEntry
*)((UInt8
*)dataQueue
->queue
+ tail
))->size
= dataSize
;
244 memcpy(&dataQueue
->queue
->data
, data
, dataSize
);
245 OSCompareAndSwap(dataQueue
->tail
, entrySize
, &dataQueue
->tail
);
249 return false; // queue is full
254 // Do not allow the tail to catch up to the head when the queue is full.
255 // That's why the comparison uses a '>' rather than '>='.
257 if ( (head
- tail
) > entrySize
)
259 entry
= (IODataQueueEntry
*)((UInt8
*)dataQueue
->queue
+ tail
);
261 entry
->size
= dataSize
;
262 memcpy(&entry
->data
, data
, dataSize
);
263 OSAddAtomic(entrySize
, (SInt32
*)&dataQueue
->tail
);
267 return false; // queue is full
271 // Send notification (via mach message) that data is available.
273 if ( ( head
== tail
) /* queue was empty prior to enqueue() */
274 || ( dataQueue
->head
== tail
) ) /* queue was emptied during enqueue() */
276 sendDataAvailableNotification();
282 Boolean
IOSharedDataQueue::dequeue(void *data
, UInt32
*dataSize
)
284 Boolean retVal
= TRUE
;
285 IODataQueueEntry
* entry
= 0;
286 UInt32 entrySize
= 0;
287 UInt32 newHeadOffset
= 0;
290 if (dataQueue
->head
!= dataQueue
->tail
) {
291 IODataQueueEntry
* head
= 0;
293 UInt32 headOffset
= dataQueue
->head
;
294 UInt32 queueSize
= getQueueSize();
296 if (headOffset
> queueSize
) {
300 head
= (IODataQueueEntry
*)((char *)dataQueue
->queue
+ headOffset
);
301 headSize
= head
->size
;
303 // we wrapped around to beginning, so read from there
304 // either there was not even room for the header
305 if ((headOffset
> UINT32_MAX
- DATA_QUEUE_ENTRY_HEADER_SIZE
) ||
306 (headOffset
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> queueSize
) ||
307 // or there was room for the header, but not for the data
308 (headOffset
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> UINT32_MAX
- headSize
) ||
309 (headOffset
+ headSize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> queueSize
)) {
310 // Note: we have to wrap to the beginning even with the UINT32_MAX checks
311 // because we have to support a queueSize of UINT32_MAX.
312 entry
= dataQueue
->queue
;
313 entrySize
= entry
->size
;
314 if ((entrySize
> UINT32_MAX
- DATA_QUEUE_ENTRY_HEADER_SIZE
) ||
315 (entrySize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> queueSize
)) {
318 newHeadOffset
= entrySize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
;
319 // else it is at the end
322 entrySize
= entry
->size
;
323 if ((entrySize
> UINT32_MAX
- DATA_QUEUE_ENTRY_HEADER_SIZE
) ||
324 (entrySize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> UINT32_MAX
- headOffset
) ||
325 (entrySize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
+ headOffset
> queueSize
)) {
328 newHeadOffset
= headOffset
+ entrySize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
;
335 if (entrySize
<= *dataSize
) {
336 memcpy(data
, &(entry
->data
), entrySize
);
337 OSCompareAndSwap( dataQueue
->head
, newHeadOffset
, (SInt32
*)&dataQueue
->head
);
345 OSCompareAndSwap( dataQueue
->head
, newHeadOffset
, (SInt32
*)&dataQueue
->head
);
349 *dataSize
= entrySize
;
361 UInt32
IOSharedDataQueue::getQueueSize()
366 return _reserved
->queueSize
;
369 Boolean
IOSharedDataQueue::setQueueSize(UInt32 size
)
374 _reserved
->queueSize
= size
;
378 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 0);
379 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 1);
380 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 2);
381 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 3);
382 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 4);
383 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 5);
384 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 6);
385 OSMetaClassDefineReservedUnused(IOSharedDataQueue
, 7);