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