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