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