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 #define DISABLE_DATAQUEUE_WARNING
31 #include <IOKit/IODataQueue.h>
33 #undef DISABLE_DATAQUEUE_WARNING
35 #include <IOKit/IODataQueueShared.h>
36 #include <IOKit/IOLib.h>
37 #include <IOKit/IOMemoryDescriptor.h>
38 #include <libkern/OSAtomic.h>
48 #define super OSObject
50 OSDefineMetaClassAndStructors(IODataQueue
, OSObject
)
52 IODataQueue
*IODataQueue::withCapacity(UInt32 size
)
54 IODataQueue
*dataQueue
= new IODataQueue
;
57 if (!dataQueue
->initWithCapacity(size
)) {
66 IODataQueue
*IODataQueue::withEntries(UInt32 numEntries
, UInt32 entrySize
)
68 IODataQueue
*dataQueue
= new IODataQueue
;
71 if (!dataQueue
->initWithEntries(numEntries
, entrySize
)) {
80 Boolean
IODataQueue::initWithCapacity(UInt32 size
)
82 vm_size_t allocSize
= 0;
88 if (size
> UINT32_MAX
- DATA_QUEUE_MEMORY_HEADER_SIZE
) {
92 allocSize
= round_page(size
+ DATA_QUEUE_MEMORY_HEADER_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;
109 notifyMsg
= IOMalloc(sizeof(mach_msg_header_t
));
113 bzero(notifyMsg
, sizeof(mach_msg_header_t
));
118 Boolean
IODataQueue::initWithEntries(UInt32 numEntries
, UInt32 entrySize
)
120 // Checking overflow for (numEntries + 1)*(entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE):
121 // check (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE)
122 if ((entrySize
> UINT32_MAX
- DATA_QUEUE_ENTRY_HEADER_SIZE
) ||
123 // check (numEntries + 1)
124 (numEntries
> UINT32_MAX
-1) ||
125 // check (numEntries + 1)*(entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE)
126 (entrySize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
> UINT32_MAX
/(numEntries
+1))) {
130 return (initWithCapacity((numEntries
+ 1) * (DATA_QUEUE_ENTRY_HEADER_SIZE
+ entrySize
)));
133 void IODataQueue::free()
136 IOFreeAligned(dataQueue
, round_page(dataQueue
->queueSize
+ DATA_QUEUE_MEMORY_HEADER_SIZE
));
140 IOFree(notifyMsg
, sizeof(mach_msg_header_t
));
150 Boolean
IODataQueue::enqueue(void * data
, UInt32 dataSize
)
152 const UInt32 head
= dataQueue
->head
; // volatile
153 const UInt32 tail
= dataQueue
->tail
;
154 const UInt32 entrySize
= dataSize
+ DATA_QUEUE_ENTRY_HEADER_SIZE
;
155 IODataQueueEntry
* entry
;
157 // Check for overflow of entrySize
158 if (dataSize
> UINT32_MAX
- DATA_QUEUE_ENTRY_HEADER_SIZE
) {
161 // Check for underflow of (dataQueue->queueSize - tail)
162 if (dataQueue
->queueSize
< tail
) {
168 // Is there enough room at the end for the entry?
169 if ((entrySize
<= UINT32_MAX
- tail
) &&
170 ((tail
+ entrySize
) <= dataQueue
->queueSize
) )
172 entry
= (IODataQueueEntry
*)((UInt8
*)dataQueue
->queue
+ tail
);
174 entry
->size
= dataSize
;
175 memcpy(&entry
->data
, data
, dataSize
);
177 // The tail can be out of bound when the size of the new entry
178 // exactly matches the available space at the end of the queue.
179 // The tail can range from 0 to dataQueue->queueSize inclusive.
181 OSAddAtomic(entrySize
, (SInt32
*)&dataQueue
->tail
);
183 else if ( head
> entrySize
) // Is there enough room at the beginning?
185 // Wrap around to the beginning, but do not allow the tail to catch
188 dataQueue
->queue
->size
= dataSize
;
190 // We need to make sure that there is enough room to set the size before
191 // doing this. The user client checks for this and will look for the size
192 // at the beginning if there isn't room for it at the end.
194 if ( ( dataQueue
->queueSize
- tail
) >= DATA_QUEUE_ENTRY_HEADER_SIZE
)
196 ((IODataQueueEntry
*)((UInt8
*)dataQueue
->queue
+ tail
))->size
= dataSize
;
199 memcpy(&dataQueue
->queue
->data
, data
, dataSize
);
200 OSCompareAndSwap(dataQueue
->tail
, entrySize
, &dataQueue
->tail
);
204 return false; // queue is full
209 // Do not allow the tail to catch up to the head when the queue is full.
210 // That's why the comparison uses a '>' rather than '>='.
212 if ( (head
- tail
) > entrySize
)
214 entry
= (IODataQueueEntry
*)((UInt8
*)dataQueue
->queue
+ tail
);
216 entry
->size
= dataSize
;
217 memcpy(&entry
->data
, data
, dataSize
);
218 OSAddAtomic(entrySize
, (SInt32
*)&dataQueue
->tail
);
222 return false; // queue is full
226 // Send notification (via mach message) that data is available.
228 if ( ( head
== tail
) /* queue was empty prior to enqueue() */
229 || ( dataQueue
->head
== tail
) ) /* queue was emptied during enqueue() */
231 sendDataAvailableNotification();
237 void IODataQueue::setNotificationPort(mach_port_t port
)
239 mach_msg_header_t
* msgh
= (mach_msg_header_t
*) notifyMsg
;
242 bzero(msgh
, sizeof(mach_msg_header_t
));
243 msgh
->msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND
, 0);
244 msgh
->msgh_size
= sizeof(mach_msg_header_t
);
245 msgh
->msgh_remote_port
= port
;
249 void IODataQueue::sendDataAvailableNotification()
252 mach_msg_header_t
* msgh
;
254 msgh
= (mach_msg_header_t
*) notifyMsg
;
255 if (msgh
&& msgh
->msgh_remote_port
) {
256 kr
= mach_msg_send_from_kernel_with_options(msgh
, msgh
->msgh_size
, MACH_SEND_TIMEOUT
, MACH_MSG_TIMEOUT_NONE
);
258 case MACH_SEND_TIMED_OUT
: // Notification already sent
259 case MACH_MSG_SUCCESS
:
260 case MACH_SEND_NO_BUFFER
:
263 IOLog("%s: dataAvailableNotification failed - msg_send returned: %d\n", /*getName()*/"IODataQueue", kr
);
269 IOMemoryDescriptor
*IODataQueue::getMemoryDescriptor()
271 IOMemoryDescriptor
*descriptor
= 0;
273 if (dataQueue
!= 0) {
274 descriptor
= IOMemoryDescriptor::withAddress(dataQueue
, dataQueue
->queueSize
+ DATA_QUEUE_MEMORY_HEADER_SIZE
, kIODirectionOutIn
);