1 /* 2 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files or portions 6 * thereof (the "Software"), to deal in the Software without restriction, 7 * including without limitation the rights to use, copy, modify, merge, 8 * publish, distribute, sublicense, and/or sell copies of the Software, 9 * and to permit persons to whom the Software is furnished to do so, subject 10 * to the following conditions: 11 * 12 * * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * * Redistributions in binary form must reproduce the above copyright notice 16 * in the binary, as well as this list of conditions and the following 17 * disclaimer in the documentation and/or other materials provided with 18 * the distribution. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 * THE SOFTWARE. 27 * 28 */ 29 30 #include <BufferGroup.h> 31 #include <Buffer.h> 32 #include "debug.h" 33 #include "SharedBufferList.h" 34 #include "DataExchange.h" 35 36 /************************************************************* 37 * private BBufferGroup 38 *************************************************************/ 39 40 status_t 41 BBufferGroup::InitBufferGroup() 42 { 43 CALLED(); 44 45 // some defaults 46 fBufferList = 0; 47 fReclaimSem = B_ERROR; 48 fInitError = B_ERROR; 49 fRequestError = B_ERROR; 50 fBufferCount = 0; 51 52 // create the reclaim semaphore 53 fReclaimSem = create_sem(0,"buffer reclaim sem"); 54 if (fReclaimSem < B_OK) { 55 ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n"); 56 fInitError = (status_t)fReclaimSem; 57 return fInitError; 58 } 59 60 // ask media_server to get the area_id of the shared buffer list 61 server_get_shared_buffer_area_request area_request; 62 server_get_shared_buffer_area_reply area_reply; 63 if (QueryServer(SERVER_GET_SHARED_BUFFER_AREA, &area_request, sizeof(area_request), &area_reply, sizeof(area_reply)) != B_OK) { 64 ERROR("BBufferGroup::InitBufferGroup: SERVER_GET_SHARED_BUFFER_AREA failed\n"); 65 fInitError = B_ERROR; 66 return fInitError; 67 } 68 ASSERT(area_reply.area > 0); 69 70 fBufferList = _shared_buffer_list::Clone(area_reply.area); 71 if (fBufferList == NULL) { 72 ERROR("BBufferGroup::InitBufferGroup: _shared_buffer_list::Clone failed\n"); 73 fInitError = B_ERROR; 74 return fInitError; 75 } 76 77 fInitError = B_OK; 78 return fInitError; 79 } 80 81 82 /************************************************************* 83 * public BBufferGroup 84 *************************************************************/ 85 86 BBufferGroup::BBufferGroup(size_t size, 87 int32 count, 88 uint32 placement, 89 uint32 lock) 90 { 91 CALLED(); 92 if (InitBufferGroup() != B_OK) 93 return; 94 95 // This one is easy. We need to create "count" BBuffers, 96 // each one "size" bytes large. They all go into one 97 // area, with "placement" and "lock" attributes. 98 // The BBuffers created will clone the area, and 99 // then we delete our area. This way BBuffers are 100 // independent from the BBufferGroup 101 102 void *start_addr; 103 area_id buffer_area; 104 size_t area_size; 105 buffer_clone_info bci; 106 BBuffer *buffer; 107 108 // don't allow all placement parameter values 109 if (placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS) { 110 ERROR("BBufferGroup: placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS (0x%08lx)\n",placement); 111 placement = B_ANY_ADDRESS; 112 } 113 114 // first we roundup for a better placement in memory 115 size = (size + 63) & ~63; 116 117 // now we create the area 118 area_size = ((size * count) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1); 119 120 buffer_area = create_area("some buffers area", &start_addr,placement,area_size,lock,B_READ_AREA | B_WRITE_AREA); 121 if (buffer_area < B_OK) { 122 ERROR("BBufferGroup: failed to allocate %ld bytes area\n",area_size); 123 fInitError = (status_t)buffer_area; 124 return; 125 } 126 127 fBufferCount = count; 128 129 for (int32 i = 0; i < count; i++) { 130 bci.area = buffer_area; 131 bci.offset = i * size; 132 bci.size = size; 133 buffer = new BBuffer(bci); 134 if (0 == buffer->Data()) { 135 // BBuffer::Data() will return 0 if an error occured 136 ERROR("BBufferGroup: error while creating buffer\n"); 137 delete buffer; 138 fInitError = B_ERROR; 139 break; 140 } 141 if (B_OK != fBufferList->AddBuffer(fReclaimSem,buffer)) { 142 ERROR("BBufferGroup: error when adding buffer\n"); 143 delete buffer; 144 fInitError = B_ERROR; 145 break; 146 } 147 } 148 149 delete_area(buffer_area); 150 } 151 152 /* explicit */ 153 BBufferGroup::BBufferGroup() 154 { 155 CALLED(); 156 if (InitBufferGroup() != B_OK) 157 return; 158 159 // this one simply creates an empty BBufferGroup 160 } 161 162 163 BBufferGroup::BBufferGroup(int32 count, 164 const media_buffer_id *buffers) 165 { 166 CALLED(); 167 if (InitBufferGroup() != B_OK) 168 return; 169 170 // XXX we need to make sure that a media_buffer_id is only added once to each group 171 172 // this one creates "BBuffer"s from "media_buffer_id"s passed 173 // by the application. 174 175 fBufferCount = count; 176 177 buffer_clone_info bci; 178 BBuffer *buffer; 179 for (int32 i = 0; i < count; i++) { 180 bci.buffer = buffers[i]; 181 buffer = new BBuffer(bci); 182 if (0 == buffer->Data()) { 183 // BBuffer::Data() will return 0 if an error occured 184 ERROR("BBufferGroup(2): error while creating buffer\n"); 185 delete buffer; 186 fInitError = B_ERROR; 187 break; 188 } 189 if (B_OK != fBufferList->AddBuffer(fReclaimSem,buffer)) { 190 ERROR("BBufferGroup(2): error when adding buffer\n"); 191 delete buffer; 192 fInitError = B_ERROR; 193 break; 194 } 195 } 196 } 197 198 199 BBufferGroup::~BBufferGroup() 200 { 201 CALLED(); 202 if (fBufferList) 203 fBufferList->Terminate(fReclaimSem); 204 if (fReclaimSem >= B_OK) 205 delete_sem(fReclaimSem); 206 } 207 208 209 status_t 210 BBufferGroup::InitCheck() 211 { 212 CALLED(); 213 return fInitError; 214 } 215 216 217 status_t 218 BBufferGroup::AddBuffer(const buffer_clone_info &info, 219 BBuffer **out_buffer) 220 { 221 CALLED(); 222 if (fInitError != B_OK) 223 return B_NO_INIT; 224 225 // XXX we need to make sure that a media_buffer_id is only added once to each group 226 227 BBuffer *buffer; 228 buffer = new BBuffer(info); 229 if (0 == buffer->Data()) { 230 // BBuffer::Data() will return 0 if an error occured 231 ERROR("BBufferGroup::AddBuffer: error while creating buffer\n"); 232 delete buffer; 233 return B_ERROR; 234 } 235 if (B_OK != fBufferList->AddBuffer(fReclaimSem,buffer)) { 236 ERROR("BBufferGroup::AddBuffer: error when adding buffer\n"); 237 delete buffer; 238 fInitError = B_ERROR; 239 return B_ERROR; 240 } 241 atomic_add(&fBufferCount,1); 242 243 if (out_buffer != 0) 244 *out_buffer = buffer; 245 246 return B_OK; 247 } 248 249 250 BBuffer * 251 BBufferGroup::RequestBuffer(size_t size, 252 bigtime_t timeout) 253 { 254 CALLED(); 255 if (fInitError != B_OK) 256 return NULL; 257 258 if (size <= 0) 259 return NULL; 260 261 BBuffer *buffer; 262 status_t status; 263 264 buffer = NULL; 265 status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, size, 0, &buffer, timeout); 266 fRequestError = status; 267 268 return (status == B_OK) ? buffer : NULL; 269 } 270 271 272 status_t 273 BBufferGroup::RequestBuffer(BBuffer *buffer, 274 bigtime_t timeout) 275 { 276 CALLED(); 277 if (fInitError != B_OK) 278 return B_NO_INIT; 279 280 if (buffer == NULL) 281 return B_BAD_VALUE; 282 283 status_t status; 284 status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0, &buffer, timeout); 285 fRequestError = status; 286 287 return status; 288 } 289 290 291 status_t 292 BBufferGroup::RequestError() 293 { 294 CALLED(); 295 if (fInitError != B_OK) 296 return B_NO_INIT; 297 298 return fRequestError; 299 } 300 301 302 status_t 303 BBufferGroup::CountBuffers(int32 *out_count) 304 { 305 CALLED(); 306 if (fInitError != B_OK) 307 return B_NO_INIT; 308 309 *out_count = fBufferCount; 310 return B_OK; 311 } 312 313 314 status_t 315 BBufferGroup::GetBufferList(int32 buf_count, 316 BBuffer **out_buffers) 317 { 318 CALLED(); 319 if (fInitError != B_OK) 320 return B_NO_INIT; 321 322 if (buf_count <= 0 || buf_count > fBufferCount) 323 return B_BAD_VALUE; 324 325 return fBufferList->GetBufferList(fReclaimSem,buf_count,out_buffers); 326 } 327 328 329 status_t 330 BBufferGroup::WaitForBuffers() 331 { 332 CALLED(); 333 if (fInitError != B_OK) 334 return B_NO_INIT; 335 336 // XXX this function is not really useful anyway, and will 337 // XXX not work exactly as documented, but it is close enough 338 339 if (fBufferCount < 0) 340 return B_BAD_VALUE; 341 if (fBufferCount == 0) 342 return B_OK; 343 344 // we need to wait until at least one buffer belonging to this group is reclaimed. 345 // this has happened when can aquire "fReclaimSem" 346 347 status_t status; 348 while (B_INTERRUPTED == (status = acquire_sem(fReclaimSem))) 349 ; 350 351 if (status != B_OK) // some error happened 352 return status; 353 354 // we need to release the "fReclaimSem" now, else we would block requesting of new buffers 355 356 return release_sem(fReclaimSem); 357 } 358 359 360 status_t 361 BBufferGroup::ReclaimAllBuffers() 362 { 363 CALLED(); 364 if (fInitError != B_OK) 365 return B_NO_INIT; 366 367 // because additional BBuffers might get added to this group betweeen acquire and release 368 int32 count = fBufferCount; 369 370 if (count < 0) 371 return B_BAD_VALUE; 372 if (count == 0) 373 return B_OK; 374 375 // we need to wait until all BBuffers belonging to this group are reclaimed. 376 // this has happened when the "fReclaimSem" can be aquired "fBufferCount" times 377 378 status_t status; 379 while (B_INTERRUPTED == (status = acquire_sem_etc(fReclaimSem, count, 0, 0))) 380 ; 381 382 if (status != B_OK) // some error happened 383 return status; 384 385 // we need to release the "fReclaimSem" now, else we would block requesting of new buffers 386 387 return release_sem_etc(fReclaimSem, count, 0); 388 } 389 390 /************************************************************* 391 * private BBufferGroup 392 *************************************************************/ 393 394 /* not implemented */ 395 /* 396 BBufferGroup::BBufferGroup(const BBufferGroup &) 397 BBufferGroup & BBufferGroup::operator=(const BBufferGroup &) 398 */ 399 400 status_t 401 BBufferGroup::AddBuffersTo(BMessage * message, const char * name, bool needLock) 402 { 403 CALLED(); 404 if (fInitError != B_OK) 405 return B_NO_INIT; 406 407 // BeOS R4 legacy API. Implemented as a wrapper around GetBufferList 408 // "needLock" is ignored, GetBufferList will do locking 409 410 if (message == NULL) 411 return B_BAD_VALUE; 412 413 if (name == NULL || strlen(name) == 0) 414 return B_BAD_VALUE; 415 416 BBuffer ** buffers; 417 status_t status; 418 int32 count; 419 420 count = fBufferCount; 421 buffers = new BBuffer * [count]; 422 423 if (B_OK != (status = GetBufferList(count,buffers))) 424 goto end; 425 426 for (int32 i = 0; i < count; i++) 427 if (B_OK != (status = message->AddInt32(name,int32(buffers[i]->ID())))) 428 goto end; 429 430 end: 431 delete [] buffers; 432 return status; 433 } 434 435