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 45OSDefineMetaClassAndStructors(IODataQueue, OSObject) 46 47IODataQueue *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 61IODataQueue *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 75Boolean 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 105Boolean 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 120void 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 131Boolean 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 218void 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 238void 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 258IOMemoryDescriptor *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 269