]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IODataQueue.cpp
1dd0c82a38b2a02fb6ded999c8b1161554a0e3e3
[apple/xnu.git] / iokit / Kernel / IODataQueue.cpp
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/IODataQueue.h>
30 #include <IOKit/IODataQueueShared.h>
31 #include <IOKit/IOLib.h>
32 #include <IOKit/IOMemoryDescriptor.h>
33 #include <libkern/OSAtomic.h>
34
35 #ifdef enqueue
36 #undef enqueue
37 #endif
38
39 #ifdef dequeue
40 #undef dequeue
41 #endif
42
43 #define super OSObject
44
45 OSDefineMetaClassAndStructors(IODataQueue, OSObject)
46
47 IODataQueue *IODataQueue::withCapacity(UInt32 size)
48 {
49 IODataQueue *dataQueue = new IODataQueue;
50
51 if (dataQueue) {
52 if (!dataQueue->initWithCapacity(size)) {
53 dataQueue->release();
54 dataQueue = 0;
55 }
56 }
57
58 return dataQueue;
59 }
60
61 IODataQueue *IODataQueue::withEntries(UInt32 numEntries, UInt32 entrySize)
62 {
63 IODataQueue *dataQueue = new IODataQueue;
64
65 if (dataQueue) {
66 if (!dataQueue->initWithEntries(numEntries, entrySize)) {
67 dataQueue->release();
68 dataQueue = 0;
69 }
70 }
71
72 return dataQueue;
73 }
74
75 Boolean IODataQueue::initWithCapacity(UInt32 size)
76 {
77 vm_size_t allocSize = 0;
78
79 if (!super::init()) {
80 return false;
81 }
82
83 if (size > UINT32_MAX - DATA_QUEUE_MEMORY_HEADER_SIZE) {
84 return false;
85 }
86
87 allocSize = round_page(size + DATA_QUEUE_MEMORY_HEADER_SIZE);
88
89 if (allocSize < size) {
90 return false;
91 }
92
93 dataQueue = (IODataQueueMemory *)IOMallocAligned(allocSize, PAGE_SIZE);
94 if (dataQueue == 0) {
95 return false;
96 }
97
98 dataQueue->queueSize = size;
99 dataQueue->head = 0;
100 dataQueue->tail = 0;
101
102 return true;
103 }
104
105 Boolean IODataQueue::initWithEntries(UInt32 numEntries, UInt32 entrySize)
106 {
107 // Checking overflow for (numEntries + 1)*(entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE):
108 // check (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE)
109 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
110 // check (numEntries + 1)
111 (numEntries > UINT32_MAX-1) ||
112 // check (numEntries + 1)*(entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE)
113 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX/(numEntries+1))) {
114 return false;
115 }
116
117 return (initWithCapacity((numEntries + 1) * (DATA_QUEUE_ENTRY_HEADER_SIZE + entrySize)));
118 }
119
120 void IODataQueue::free()
121 {
122 if (dataQueue) {
123 IOFreeAligned(dataQueue, round_page(dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE));
124 }
125
126 super::free();
127
128 return;
129 }
130
131 Boolean IODataQueue::enqueue(void * data, UInt32 dataSize)
132 {
133 const UInt32 head = dataQueue->head; // volatile
134 const UInt32 tail = dataQueue->tail;
135 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;
136 IODataQueueEntry * entry;
137
138 // Check for overflow of entrySize
139 if (dataSize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) {
140 return false;
141 }
142 // Check for underflow of (dataQueue->queueSize - tail)
143 if (dataQueue->queueSize < tail) {
144 return false;
145 }
146
147 if ( tail >= head )
148 {
149 // Is there enough room at the end for the entry?
150 if ((entrySize <= UINT32_MAX - tail) &&
151 ((tail + entrySize) <= dataQueue->queueSize) )
152 {
153 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
154
155 entry->size = dataSize;
156 memcpy(&entry->data, data, dataSize);
157
158 // The tail can be out of bound when the size of the new entry
159 // exactly matches the available space at the end of the queue.
160 // The tail can range from 0 to dataQueue->queueSize inclusive.
161
162 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail);
163 }
164 else if ( head > entrySize ) // Is there enough room at the beginning?
165 {
166 // Wrap around to the beginning, but do not allow the tail to catch
167 // up to the head.
168
169 dataQueue->queue->size = dataSize;
170
171 // We need to make sure that there is enough room to set the size before
172 // doing this. The user client checks for this and will look for the size
173 // at the beginning if there isn't room for it at the end.
174
175 if ( ( dataQueue->queueSize - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE )
176 {
177 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;
178 }
179
180 memcpy(&dataQueue->queue->data, data, dataSize);
181 OSCompareAndSwap(dataQueue->tail, entrySize, &dataQueue->tail);
182 }
183 else
184 {
185 return false; // queue is full
186 }
187 }
188 else
189 {
190 // Do not allow the tail to catch up to the head when the queue is full.
191 // That's why the comparison uses a '>' rather than '>='.
192
193 if ( (head - tail) > entrySize )
194 {
195 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
196
197 entry->size = dataSize;
198 memcpy(&entry->data, data, dataSize);
199 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail);
200 }
201 else
202 {
203 return false; // queue is full
204 }
205 }
206
207 // Send notification (via mach message) that data is available.
208
209 if ( ( head == tail ) /* queue was empty prior to enqueue() */
210 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */
211 {
212 sendDataAvailableNotification();
213 }
214
215 return true;
216 }
217
218 void IODataQueue::setNotificationPort(mach_port_t port)
219 {
220 static struct _notifyMsg init_msg = { {
221 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
222 sizeof (struct _notifyMsg),
223 MACH_PORT_NULL,
224 MACH_PORT_NULL,
225 0,
226 0
227 } };
228
229 if (notifyMsg == 0) {
230 notifyMsg = IOMalloc(sizeof(struct _notifyMsg));
231 }
232
233 *((struct _notifyMsg *)notifyMsg) = init_msg;
234
235 ((struct _notifyMsg *)notifyMsg)->h.msgh_remote_port = port;
236 }
237
238 void IODataQueue::sendDataAvailableNotification()
239 {
240 kern_return_t kr;
241 mach_msg_header_t * msgh;
242
243 msgh = (mach_msg_header_t *)notifyMsg;
244 if (msgh && msgh->msgh_remote_port) {
245 kr = mach_msg_send_from_kernel_with_options(msgh, msgh->msgh_size, MACH_SEND_TIMEOUT, MACH_MSG_TIMEOUT_NONE);
246 switch(kr) {
247 case MACH_SEND_TIMED_OUT: // Notification already sent
248 case MACH_MSG_SUCCESS:
249 case MACH_SEND_NO_BUFFER:
250 break;
251 default:
252 IOLog("%s: dataAvailableNotification failed - msg_send returned: %d\n", /*getName()*/"IODataQueue", kr);
253 break;
254 }
255 }
256 }
257
258 IOMemoryDescriptor *IODataQueue::getMemoryDescriptor()
259 {
260 IOMemoryDescriptor *descriptor = 0;
261
262 if (dataQueue != 0) {
263 descriptor = IOMemoryDescriptor::withAddress(dataQueue, dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE, kIODirectionOutIn);
264 }
265
266 return descriptor;
267 }
268