1 /*- 2 * Copyright (c) 2000 Michael Smith 3 * Copyright (c) 2000 BSDi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: src/sys/dev/acpica/acpi_resource.c,v 1.16.4.1 2003/08/22 20:49:20 jhb Exp $ 28 * $DragonFly: src/sys/dev/acpica/Attic/acpi_resource.c,v 1.1 2003/09/24 03:32:16 drhodus Exp $ 29 */ 30 31 #include "opt_acpi.h" 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/bus.h> 35 36 #include <machine/bus.h> 37 #include <machine/resource.h> 38 #include <sys/rman.h> 39 40 #include "acpi.h" 41 42 #include <dev/acpica/acpivar.h> 43 44 /* 45 * Hooks for the ACPI CA debugging infrastructure 46 */ 47 #define _COMPONENT ACPI_BUS 48 ACPI_MODULE_NAME("RESOURCE") 49 50 /* 51 * Fetch a device's resources and associate them with the device. 52 * 53 * Note that it might be nice to also locate ACPI-specific resource items, such 54 * as GPE bits. 55 * 56 * We really need to split the resource-fetching code out from the 57 * resource-parsing code, since we may want to use the parsing 58 * code for _PRS someday. 59 */ 60 ACPI_STATUS 61 acpi_parse_resources(device_t dev, ACPI_HANDLE handle, struct acpi_parse_resource_set *set) 62 { 63 ACPI_BUFFER buf; 64 ACPI_RESOURCE *res; 65 char *curr, *last; 66 ACPI_STATUS status; 67 void *context; 68 69 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 70 71 /* 72 * Special-case some devices that abuse _PRS/_CRS to mean 73 * something other than "I consume this resource". 74 * 75 * XXX do we really need this? It's only relevant once 76 * we start always-allocating these resources, and even 77 * then, the only special-cased device is likely to be 78 * the PCI interrupt link. 79 */ 80 81 /* 82 * Fetch the device's current resources. 83 */ 84 buf.Length = ACPI_ALLOCATE_BUFFER; 85 if (ACPI_FAILURE((status = AcpiGetCurrentResources(handle, &buf)))) { 86 if (status != AE_NOT_FOUND) 87 printf("can't fetch resources for %s - %s\n", 88 acpi_name(handle), AcpiFormatException(status)); 89 return_ACPI_STATUS(status); 90 } 91 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "%s - got %ld bytes of resources\n", 92 acpi_name(handle), (long)buf.Length)); 93 set->set_init(dev, &context); 94 95 /* 96 * Iterate through the resources 97 */ 98 curr = buf.Pointer; 99 last = (char *)buf.Pointer + buf.Length; 100 while (curr < last) { 101 res = (ACPI_RESOURCE *)curr; 102 curr += res->Length; 103 104 /* 105 * Handle the individual resource types 106 */ 107 switch(res->Id) { 108 case ACPI_RSTYPE_END_TAG: 109 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "EndTag\n")); 110 curr = last; 111 break; 112 113 case ACPI_RSTYPE_FIXED_IO: 114 if (res->Data.FixedIo.RangeLength <= 0) 115 break; 116 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "FixedIo 0x%x/%d\n", 117 res->Data.FixedIo.BaseAddress, 118 res->Data.FixedIo.RangeLength)); 119 set->set_ioport(dev, context, 120 res->Data.FixedIo.BaseAddress, 121 res->Data.FixedIo.RangeLength); 122 break; 123 124 case ACPI_RSTYPE_IO: 125 if (res->Data.Io.RangeLength <= 0) 126 break; 127 if (res->Data.Io.MinBaseAddress == res->Data.Io.MaxBaseAddress) { 128 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Io 0x%x/%d\n", 129 res->Data.Io.MinBaseAddress, 130 res->Data.Io.RangeLength)); 131 set->set_ioport(dev, context, 132 res->Data.Io.MinBaseAddress, 133 res->Data.Io.RangeLength); 134 } else { 135 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Io 0x%x-0x%x/%d\n", 136 res->Data.Io.MinBaseAddress, 137 res->Data.Io.MaxBaseAddress, 138 res->Data.Io.RangeLength)); 139 set->set_iorange(dev, context, 140 res->Data.Io.MinBaseAddress, 141 res->Data.Io.MaxBaseAddress, 142 res->Data.Io.RangeLength, res->Data.Io.Alignment); 143 } 144 break; 145 146 case ACPI_RSTYPE_FIXED_MEM32: 147 if (res->Data.FixedMemory32.RangeLength <= 0) 148 break; 149 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "FixedMemory32 0x%x/%d\n", 150 res->Data.FixedMemory32.RangeBaseAddress, 151 res->Data.FixedMemory32.RangeLength)); 152 set->set_memory(dev, context, res->Data.FixedMemory32.RangeBaseAddress, 153 res->Data.FixedMemory32.RangeLength); 154 break; 155 156 case ACPI_RSTYPE_MEM32: 157 if (res->Data.Memory32.RangeLength <= 0) 158 break; 159 if (res->Data.Memory32.MinBaseAddress == res->Data.Memory32.MaxBaseAddress) { 160 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory32 0x%x/%d\n", 161 res->Data.Memory32.MinBaseAddress, 162 res->Data.Memory32.RangeLength)); 163 set->set_memory(dev, context, 164 res->Data.Memory32.MinBaseAddress, 165 res->Data.Memory32.RangeLength); 166 } else { 167 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory32 0x%x-0x%x/%d\n", 168 res->Data.Memory32.MinBaseAddress, 169 res->Data.Memory32.MaxBaseAddress, 170 res->Data.Memory32.RangeLength)); 171 set->set_memoryrange(dev, context, 172 res->Data.Memory32.MinBaseAddress, 173 res->Data.Memory32.MaxBaseAddress, 174 res->Data.Memory32.RangeLength, 175 res->Data.Memory32.Alignment); 176 } 177 break; 178 179 case ACPI_RSTYPE_MEM24: 180 if (res->Data.Memory24.RangeLength <= 0) 181 break; 182 if (res->Data.Memory24.MinBaseAddress == res->Data.Memory24.MaxBaseAddress) { 183 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory24 0x%x/%d\n", 184 res->Data.Memory24.MinBaseAddress, 185 res->Data.Memory24.RangeLength)); 186 set->set_memory(dev, context, res->Data.Memory24.MinBaseAddress, 187 res->Data.Memory24.RangeLength); 188 } else { 189 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory24 0x%x-0x%x/%d\n", 190 res->Data.Memory24.MinBaseAddress, 191 res->Data.Memory24.MaxBaseAddress, 192 res->Data.Memory24.RangeLength)); 193 set->set_memoryrange(dev, context, 194 res->Data.Memory24.MinBaseAddress, 195 res->Data.Memory24.MaxBaseAddress, 196 res->Data.Memory24.RangeLength, 197 res->Data.Memory24.Alignment); 198 } 199 break; 200 201 case ACPI_RSTYPE_IRQ: 202 /* 203 * from 1.0b 6.4.2 204 * "This structure is repeated for each separate interrupt 205 * required" 206 */ 207 set->set_irq(dev, context, res->Data.Irq.Interrupts, 208 res->Data.Irq.NumberOfInterrupts); 209 break; 210 211 case ACPI_RSTYPE_DMA: 212 /* 213 * from 1.0b 6.4.3 214 * "This structure is repeated for each separate dma channel 215 * required" 216 */ 217 218 set->set_drq(dev, context, res->Data.Dma.Channels, 219 res->Data.Dma.NumberOfChannels); 220 break; 221 222 case ACPI_RSTYPE_START_DPF: 223 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "start dependant functions\n")); 224 set->set_start_dependant(dev, context, 225 res->Data.StartDpf.CompatibilityPriority); 226 break; 227 228 case ACPI_RSTYPE_END_DPF: 229 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "end dependant functions\n")); 230 set->set_end_dependant(dev, context); 231 break; 232 233 case ACPI_RSTYPE_ADDRESS32: 234 if (res->Data.Address32.AddressLength <= 0) 235 break; 236 if (res->Data.Address32.ProducerConsumer != ACPI_CONSUMER) { 237 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "ignored Address32 %s producer\n", 238 (res->Data.Address32.ResourceType == ACPI_IO_RANGE) ? 239 "IO" : "Memory")); 240 break; 241 } 242 if (res->Data.Address32.ResourceType != ACPI_MEMORY_RANGE && 243 res->Data.Address32.ResourceType != ACPI_IO_RANGE) { 244 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, 245 "ignored Address32 for non-memory, non-I/O\n")); 246 break; 247 } 248 249 if ((res->Data.Address32.MinAddressFixed == ACPI_ADDRESS_FIXED) && 250 (res->Data.Address32.MaxAddressFixed == ACPI_ADDRESS_FIXED)) { 251 if (res->Data.Address32.ResourceType == ACPI_MEMORY_RANGE) { 252 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/Memory 0x%x/%d\n", 253 res->Data.Address32.MinAddressRange, 254 res->Data.Address32.AddressLength)); 255 set->set_memory(dev, context, 256 res->Data.Address32.MinAddressRange, 257 res->Data.Address32.AddressLength); 258 } else { 259 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/IO 0x%x/%d\n", 260 res->Data.Address32.MinAddressRange, 261 res->Data.Address32.AddressLength)); 262 set->set_ioport(dev, context, 263 res->Data.Address32.MinAddressRange, 264 res->Data.Address32.AddressLength); 265 } 266 } else { 267 if (res->Data.Address32.ResourceType == ACPI_MEMORY_RANGE) { 268 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/Memory 0x%x-0x%x/%d\n", 269 res->Data.Address32.MinAddressRange, 270 res->Data.Address32.MaxAddressRange, 271 res->Data.Address32.AddressLength)); 272 set->set_memoryrange(dev, context, 273 res->Data.Address32.MinAddressRange, 274 res->Data.Address32.MaxAddressRange, 275 res->Data.Address32.AddressLength, 276 res->Data.Address32.Granularity); 277 } else { 278 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/IO 0x%x-0x%x/%d\n", 279 res->Data.Address32.MinAddressRange, 280 res->Data.Address32.MaxAddressRange, 281 res->Data.Address32.AddressLength)); 282 set->set_iorange(dev, context, 283 res->Data.Address32.MinAddressRange, 284 res->Data.Address32.MaxAddressRange, 285 res->Data.Address32.AddressLength, 286 res->Data.Address32.Granularity); 287 } 288 } 289 break; 290 291 case ACPI_RSTYPE_ADDRESS16: 292 if (res->Data.Address16.AddressLength <= 0) 293 break; 294 if (res->Data.Address16.ProducerConsumer != ACPI_CONSUMER) { 295 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "ignored Address16 %s producer\n", 296 (res->Data.Address16.ResourceType == ACPI_IO_RANGE) ? 297 "IO" : "Memory")); 298 break; 299 } 300 if (res->Data.Address16.ResourceType != ACPI_MEMORY_RANGE && 301 res->Data.Address16.ResourceType != ACPI_IO_RANGE) { 302 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, 303 "ignored Address16 for non-memory, non-I/O\n")); 304 break; 305 } 306 307 if ((res->Data.Address16.MinAddressFixed == ACPI_ADDRESS_FIXED) && 308 (res->Data.Address16.MaxAddressFixed == ACPI_ADDRESS_FIXED)) { 309 if (res->Data.Address16.ResourceType == ACPI_MEMORY_RANGE) { 310 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/Memory 0x%x/%d\n", 311 res->Data.Address16.MinAddressRange, 312 res->Data.Address16.AddressLength)); 313 set->set_memory(dev, context, 314 res->Data.Address16.MinAddressRange, 315 res->Data.Address16.AddressLength); 316 } else { 317 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/IO 0x%x/%d\n", 318 res->Data.Address16.MinAddressRange, 319 res->Data.Address16.AddressLength)); 320 set->set_ioport(dev, context, 321 res->Data.Address16.MinAddressRange, 322 res->Data.Address16.AddressLength); 323 } 324 } else { 325 if (res->Data.Address16.ResourceType == ACPI_MEMORY_RANGE) { 326 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/Memory 0x%x-0x%x/%d\n", 327 res->Data.Address16.MinAddressRange, 328 res->Data.Address16.MaxAddressRange, 329 res->Data.Address16.AddressLength)); 330 set->set_memoryrange(dev, context, 331 res->Data.Address16.MinAddressRange, 332 res->Data.Address16.MaxAddressRange, 333 res->Data.Address16.AddressLength, 334 res->Data.Address16.Granularity); 335 } else { 336 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/IO 0x%x-0x%x/%d\n", 337 res->Data.Address16.MinAddressRange, 338 res->Data.Address16.MaxAddressRange, 339 res->Data.Address16.AddressLength)); 340 set->set_iorange(dev, context, 341 res->Data.Address16.MinAddressRange, 342 res->Data.Address16.MaxAddressRange, 343 res->Data.Address16.AddressLength, 344 res->Data.Address16.Granularity); 345 } 346 } 347 break; 348 349 case ACPI_RSTYPE_ADDRESS64: 350 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "unimplemented Address64 resource\n")); 351 break; 352 353 case ACPI_RSTYPE_EXT_IRQ: 354 /* XXX special handling? */ 355 set->set_irq(dev, context,res->Data.ExtendedIrq.Interrupts, 356 res->Data.ExtendedIrq.NumberOfInterrupts); 357 break; 358 359 case ACPI_RSTYPE_VENDOR: 360 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "unimplemented VendorSpecific resource\n")); 361 break; 362 default: 363 break; 364 } 365 } 366 AcpiOsFree(buf.Pointer); 367 set->set_done(dev, context); 368 return_ACPI_STATUS(AE_OK); 369 } 370 371 /* 372 * Resource-set vectors used to attach _CRS-derived resources 373 * to an ACPI device. 374 */ 375 static void acpi_res_set_init(device_t dev, void **context); 376 static void acpi_res_set_done(device_t dev, void *context); 377 static void acpi_res_set_ioport(device_t dev, void *context, u_int32_t base, u_int32_t length); 378 static void acpi_res_set_iorange(device_t dev, void *context, u_int32_t low, u_int32_t high, 379 u_int32_t length, u_int32_t align); 380 static void acpi_res_set_memory(device_t dev, void *context, u_int32_t base, u_int32_t length); 381 static void acpi_res_set_memoryrange(device_t dev, void *context, u_int32_t low, u_int32_t high, 382 u_int32_t length, u_int32_t align); 383 static void acpi_res_set_irq(device_t dev, void *context, u_int32_t *irq, 384 int count); 385 static void acpi_res_set_drq(device_t dev, void *context, u_int32_t *drq, 386 int count); 387 static void acpi_res_set_start_dependant(device_t dev, void *context, int preference); 388 static void acpi_res_set_end_dependant(device_t dev, void *context); 389 390 struct acpi_parse_resource_set acpi_res_parse_set = { 391 acpi_res_set_init, 392 acpi_res_set_done, 393 acpi_res_set_ioport, 394 acpi_res_set_iorange, 395 acpi_res_set_memory, 396 acpi_res_set_memoryrange, 397 acpi_res_set_irq, 398 acpi_res_set_drq, 399 acpi_res_set_start_dependant, 400 acpi_res_set_end_dependant 401 }; 402 403 struct acpi_res_context { 404 int ar_nio; 405 int ar_nmem; 406 int ar_nirq; 407 int ar_ndrq; 408 }; 409 410 static void 411 acpi_res_set_init(device_t dev, void **context) 412 { 413 struct acpi_res_context *cp; 414 415 if ((cp = AcpiOsAllocate(sizeof(*cp))) != NULL) { 416 bzero(cp, sizeof(*cp)); 417 *context = cp; 418 } 419 } 420 421 static void 422 acpi_res_set_done(device_t dev, void *context) 423 { 424 struct acpi_res_context *cp = (struct acpi_res_context *)context; 425 426 if (cp == NULL) 427 return; 428 AcpiOsFree(cp); 429 } 430 431 static void 432 acpi_res_set_ioport(device_t dev, void *context, u_int32_t base, u_int32_t length) 433 { 434 struct acpi_res_context *cp = (struct acpi_res_context *)context; 435 436 if (cp == NULL) 437 return; 438 bus_set_resource(dev, SYS_RES_IOPORT, cp->ar_nio++, base, length); 439 } 440 441 static void 442 acpi_res_set_iorange(device_t dev, void *context, u_int32_t low, u_int32_t high, u_int32_t length, u_int32_t align) 443 { 444 struct acpi_res_context *cp = (struct acpi_res_context *)context; 445 446 if (cp == NULL) 447 return; 448 device_printf(dev, "I/O range not supported\n"); 449 } 450 451 static void 452 acpi_res_set_memory(device_t dev, void *context, u_int32_t base, u_int32_t length) 453 { 454 struct acpi_res_context *cp = (struct acpi_res_context *)context; 455 456 if (cp == NULL) 457 return; 458 459 bus_set_resource(dev, SYS_RES_MEMORY, cp->ar_nmem++, base, length); 460 } 461 462 static void 463 acpi_res_set_memoryrange(device_t dev, void *context, u_int32_t low, u_int32_t high, u_int32_t length, u_int32_t align) 464 { 465 struct acpi_res_context *cp = (struct acpi_res_context *)context; 466 467 if (cp == NULL) 468 return; 469 device_printf(dev, "memory range not supported\n"); 470 } 471 472 static void 473 acpi_res_set_irq(device_t dev, void *context, u_int32_t *irq, int count) 474 { 475 struct acpi_res_context *cp = (struct acpi_res_context *)context; 476 477 if (cp == NULL) 478 return; 479 if (irq == NULL) 480 return; 481 482 /*This implements no resource relocation.*/ 483 if(count != 1) 484 return; 485 486 bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1); 487 } 488 489 static void 490 acpi_res_set_drq(device_t dev, void *context, u_int32_t *drq, int count) 491 { 492 struct acpi_res_context *cp = (struct acpi_res_context *)context; 493 494 if (cp == NULL) 495 return; 496 if (drq == NULL) 497 return; 498 499 /*This implements no resource relocation.*/ 500 if(count != 1) 501 return; 502 503 bus_set_resource(dev, SYS_RES_DRQ, cp->ar_ndrq++, *drq, 1); 504 } 505 506 static void 507 acpi_res_set_start_dependant(device_t dev, void *context, int preference) 508 { 509 struct acpi_res_context *cp = (struct acpi_res_context *)context; 510 511 if (cp == NULL) 512 return; 513 device_printf(dev, "dependant functions not supported\n"); 514 } 515 516 static void 517 acpi_res_set_end_dependant(device_t dev, void *context) 518 { 519 struct acpi_res_context *cp = (struct acpi_res_context *)context; 520 521 if (cp == NULL) 522 return; 523 } 524 525 /* 526 * Resource-owning placeholders. 527 * 528 * This code "owns" system resource objects that aren't 529 * otherwise useful to devices, and which shouldn't be 530 * considered "free". 531 * 532 * Note that some systems claim *all* of the physical address space 533 * with a PNP0C01 device, so we cannot correctly "own" system memory 534 * here (must be done in the SMAP handler on x86 systems, for 535 * example). 536 */ 537 538 static int acpi_sysresource_probe(device_t dev); 539 static int acpi_sysresource_attach(device_t dev); 540 541 static device_method_t acpi_sysresource_methods[] = { 542 /* Device interface */ 543 DEVMETHOD(device_probe, acpi_sysresource_probe), 544 DEVMETHOD(device_attach, acpi_sysresource_attach), 545 546 {0, 0} 547 }; 548 549 static driver_t acpi_sysresource_driver = { 550 "acpi_sysresource", 551 acpi_sysresource_methods, 552 0, 553 }; 554 555 static devclass_t acpi_sysresource_devclass; 556 DRIVER_MODULE(acpi_sysresource, acpi, acpi_sysresource_driver, acpi_sysresource_devclass, 0, 0); 557 558 static int 559 acpi_sysresource_probe(device_t dev) 560 { 561 if (acpi_disabled("sysresource")) 562 return(ENXIO); 563 if (acpi_MatchHid(dev, "PNP0C02")) { 564 device_set_desc(dev, "system resource"); 565 } else { 566 return(ENXIO); 567 } 568 device_quiet(dev); 569 return(-100); 570 } 571 572 static int 573 acpi_sysresource_attach(device_t dev) 574 { 575 struct resource *res; 576 int i, rid; 577 578 /* 579 * Suck up all the resources that might have been assigned to us. 580 * Note that it's impossible to tell the difference between a 581 * resource that someone else has claimed, and one that doesn't 582 * exist. 583 */ 584 for (i = 0; i < 100; i++) { 585 rid = i; 586 res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, 0); 587 rid = i; 588 res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 0); 589 rid = i; 590 res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE); 591 } 592 return(0); 593 } 594