]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IODataQueue.cpp
cd0dfca22a9d42fe03bd438c9cfc0509619a9779
[apple/xnu.git] / iokit / Kernel / IODataQueue.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <IOKit/IODataQueue.h>
25 #include <IOKit/IODataQueueShared.h>
26 #include <IOKit/IOLib.h>
27 #include <IOKit/IOMemoryDescriptor.h>
28
29 #ifdef enqueue
30 #undef enqueue
31 #endif
32
33 #ifdef dequeue
34 #undef dequeue
35 #endif
36
37 #define super OSObject
38
39 OSDefineMetaClassAndStructors(IODataQueue, OSObject)
40
41 IODataQueue *IODataQueue::withCapacity(UInt32 size)
42 {
43 IODataQueue *dataQueue = new IODataQueue;
44
45 if (dataQueue) {
46 if (!dataQueue->initWithCapacity(size)) {
47 dataQueue->release();
48 dataQueue = 0;
49 }
50 }
51
52 return dataQueue;
53 }
54
55 IODataQueue *IODataQueue::withEntries(UInt32 numEntries, UInt32 entrySize)
56 {
57 IODataQueue *dataQueue = new IODataQueue;
58
59 if (dataQueue) {
60 if (!dataQueue->initWithEntries(numEntries, entrySize)) {
61 dataQueue->release();
62 dataQueue = 0;
63 }
64 }
65
66 return dataQueue;
67 }
68
69 Boolean IODataQueue::initWithCapacity(UInt32 size)
70 {
71 if (!super::init()) {
72 return false;
73 }
74
75 dataQueue = (IODataQueueMemory *)IOMallocAligned(round_page_32(size + DATA_QUEUE_MEMORY_HEADER_SIZE), PAGE_SIZE);
76 if (dataQueue == 0) {
77 return false;
78 }
79
80 dataQueue->queueSize = size;
81 dataQueue->head = 0;
82 dataQueue->tail = 0;
83
84 return true;
85 }
86
87 Boolean IODataQueue::initWithEntries(UInt32 numEntries, UInt32 entrySize)
88 {
89 return (initWithCapacity((numEntries + 1) * (DATA_QUEUE_ENTRY_HEADER_SIZE + entrySize)));
90 }
91
92 void IODataQueue::free()
93 {
94 if (dataQueue) {
95 IOFreeAligned(dataQueue, round_page_32(dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE));
96 }
97
98 super::free();
99
100 return;
101 }
102
103 Boolean IODataQueue::enqueue(void * data, UInt32 dataSize)
104 {
105 const UInt32 head = dataQueue->head; // volatile
106 const UInt32 tail = dataQueue->tail;
107 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;
108 IODataQueueEntry * entry;
109
110 if ( tail >= head )
111 {
112 // Is there enough room at the end for the entry?
113 if ( (tail + entrySize) <= dataQueue->queueSize )
114 {
115 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
116
117 entry->size = dataSize;
118 memcpy(&entry->data, data, dataSize);
119
120 // The tail can be out of bound when the size of the new entry
121 // exactly matches the available space at the end of the queue.
122 // The tail can range from 0 to dataQueue->queueSize inclusive.
123
124 dataQueue->tail += entrySize;
125 }
126 else if ( head > entrySize ) // Is there enough room at the beginning?
127 {
128 // Wrap around to the beginning, but do not allow the tail to catch
129 // up to the head.
130
131 dataQueue->queue->size = dataSize;
132
133 // We need to make sure that there is enough room to set the size before
134 // doing this. The user client checks for this and will look for the size
135 // at the beginning if there isn't room for it at the end.
136
137 if ( ( dataQueue->queueSize - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE )
138 {
139 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;
140 }
141
142 memcpy(&dataQueue->queue->data, data, dataSize);
143 dataQueue->tail = entrySize;
144 }
145 else
146 {
147 return false; // queue is full
148 }
149 }
150 else
151 {
152 // Do not allow the tail to catch up to the head when the queue is full.
153 // That's why the comparison uses a '>' rather than '>='.
154
155 if ( (head - tail) > entrySize )
156 {
157 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
158
159 entry->size = dataSize;
160 memcpy(&entry->data, data, dataSize);
161 dataQueue->tail += entrySize;
162 }
163 else
164 {
165 return false; // queue is full
166 }
167 }
168
169 // Send notification (via mach message) that data is available.
170
171 if ( ( head == tail ) /* queue was empty prior to enqueue() */
172 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */
173 {
174 sendDataAvailableNotification();
175 }
176
177 return true;
178 }
179
180 void IODataQueue::setNotificationPort(mach_port_t port)
181 {
182 static struct _notifyMsg init_msg = { {
183 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
184 sizeof (struct _notifyMsg),
185 MACH_PORT_NULL,
186 MACH_PORT_NULL,
187 0,
188 0
189 } };
190
191 if (notifyMsg == 0) {
192 notifyMsg = IOMalloc(sizeof(struct _notifyMsg));
193 }
194
195 *((struct _notifyMsg *)notifyMsg) = init_msg;
196
197 ((struct _notifyMsg *)notifyMsg)->h.msgh_remote_port = port;
198 }
199
200 void IODataQueue::sendDataAvailableNotification()
201 {
202 kern_return_t kr;
203 mach_msg_header_t * msgh;
204
205 msgh = (mach_msg_header_t *)notifyMsg;
206 if (msgh) {
207 kr = mach_msg_send_from_kernel(msgh, msgh->msgh_size);
208 switch(kr) {
209 case MACH_SEND_TIMED_OUT: // Notification already sent
210 case MACH_MSG_SUCCESS:
211 break;
212 default:
213 IOLog("%s: dataAvailableNotification failed - msg_send returned: %d\n", /*getName()*/"IODataQueue", kr);
214 break;
215 }
216 }
217 }
218
219 IOMemoryDescriptor *IODataQueue::getMemoryDescriptor()
220 {
221 IOMemoryDescriptor *descriptor = 0;
222
223 if (dataQueue != 0) {
224 descriptor = IOMemoryDescriptor::withAddress(dataQueue, dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE, kIODirectionOutIn);
225 }
226
227 return descriptor;
228 }
229