1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /******************************************************************************* 3 * 4 * Module Name: rsmisc - Miscellaneous resource descriptors 5 * 6 ******************************************************************************/ 7 8 #include <acpi/acpi.h> 9 #include "accommon.h" 10 #include "acresrc.h" 11 12 #define _COMPONENT ACPI_RESOURCES 13 ACPI_MODULE_NAME("rsmisc") 14 #define INIT_RESOURCE_TYPE(i) i->resource_offset 15 #define INIT_RESOURCE_LENGTH(i) i->aml_offset 16 #define INIT_TABLE_LENGTH(i) i->value 17 #define COMPARE_OPCODE(i) i->resource_offset 18 #define COMPARE_TARGET(i) i->aml_offset 19 #define COMPARE_VALUE(i) i->value 20 /******************************************************************************* 21 * 22 * FUNCTION: acpi_rs_convert_aml_to_resource 23 * 24 * PARAMETERS: resource - Pointer to the resource descriptor 25 * aml - Where the AML descriptor is returned 26 * info - Pointer to appropriate conversion table 27 * 28 * RETURN: Status 29 * 30 * DESCRIPTION: Convert an external AML resource descriptor to the corresponding 31 * internal resource descriptor 32 * 33 ******************************************************************************/ 34 acpi_status 35 acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, 36 union aml_resource *aml, 37 struct acpi_rsconvert_info *info) 38 { 39 acpi_rs_length aml_resource_length; 40 void *source; 41 void *destination; 42 char *target; 43 u8 count; 44 u8 flags_mode = FALSE; 45 u16 item_count = 0; 46 u16 temp16 = 0; 47 48 ACPI_FUNCTION_TRACE(rs_convert_aml_to_resource); 49 50 if (!info) { 51 return_ACPI_STATUS(AE_BAD_PARAMETER); 52 } 53 54 if (((acpi_size)resource) & 0x3) { 55 56 /* Each internal resource struct is expected to be 32-bit aligned */ 57 58 ACPI_WARNING((AE_INFO, 59 "Misaligned resource pointer (get): %p Type 0x%2.2X Length %u", 60 resource, resource->type, resource->length)); 61 } 62 63 /* Extract the resource Length field (does not include header length) */ 64 65 aml_resource_length = acpi_ut_get_resource_length(aml); 66 67 /* 68 * First table entry must be ACPI_RSC_INITxxx and must contain the 69 * table length (# of table entries) 70 */ 71 count = INIT_TABLE_LENGTH(info); 72 while (count) { 73 target = NULL; 74 75 /* 76 * Source is the external AML byte stream buffer, 77 * destination is the internal resource descriptor 78 */ 79 source = ACPI_ADD_PTR(void, aml, info->aml_offset); 80 destination = 81 ACPI_ADD_PTR(void, resource, info->resource_offset); 82 83 switch (info->opcode) { 84 case ACPI_RSC_INITGET: 85 /* 86 * Get the resource type and the initial (minimum) length 87 */ 88 memset(resource, 0, INIT_RESOURCE_LENGTH(info)); 89 resource->type = INIT_RESOURCE_TYPE(info); 90 resource->length = INIT_RESOURCE_LENGTH(info); 91 break; 92 93 case ACPI_RSC_INITSET: 94 break; 95 96 case ACPI_RSC_FLAGINIT: 97 98 flags_mode = TRUE; 99 break; 100 101 case ACPI_RSC_1BITFLAG: 102 /* 103 * Mask and shift the flag bit 104 */ 105 ACPI_SET8(destination, 106 ((ACPI_GET8(source) >> info->value) & 0x01)); 107 break; 108 109 case ACPI_RSC_2BITFLAG: 110 /* 111 * Mask and shift the flag bits 112 */ 113 ACPI_SET8(destination, 114 ((ACPI_GET8(source) >> info->value) & 0x03)); 115 break; 116 117 case ACPI_RSC_3BITFLAG: 118 /* 119 * Mask and shift the flag bits 120 */ 121 ACPI_SET8(destination, 122 ((ACPI_GET8(source) >> info->value) & 0x07)); 123 break; 124 125 case ACPI_RSC_6BITFLAG: 126 /* 127 * Mask and shift the flag bits 128 */ 129 ACPI_SET8(destination, 130 ((ACPI_GET8(source) >> info->value) & 0x3F)); 131 break; 132 133 case ACPI_RSC_COUNT: 134 135 item_count = ACPI_GET8(source); 136 ACPI_SET8(destination, item_count); 137 138 resource->length = resource->length + 139 (info->value * (item_count - 1)); 140 break; 141 142 case ACPI_RSC_COUNT16: 143 144 item_count = aml_resource_length; 145 ACPI_SET16(destination, item_count); 146 147 resource->length = resource->length + 148 (info->value * (item_count - 1)); 149 break; 150 151 case ACPI_RSC_COUNT_GPIO_PIN: 152 153 target = ACPI_ADD_PTR(void, aml, info->value); 154 item_count = ACPI_GET16(target) - ACPI_GET16(source); 155 156 resource->length = resource->length + item_count; 157 item_count = item_count / 2; 158 ACPI_SET16(destination, item_count); 159 break; 160 161 case ACPI_RSC_COUNT_GPIO_VEN: 162 163 item_count = ACPI_GET8(source); 164 ACPI_SET8(destination, item_count); 165 166 resource->length = 167 resource->length + (info->value * item_count); 168 break; 169 170 case ACPI_RSC_COUNT_GPIO_RES: 171 /* 172 * Vendor data is optional (length/offset may both be zero) 173 * Examine vendor data length field first 174 */ 175 target = ACPI_ADD_PTR(void, aml, (info->value + 2)); 176 if (ACPI_GET16(target)) { 177 178 /* Use vendor offset to get resource source length */ 179 180 target = ACPI_ADD_PTR(void, aml, info->value); 181 item_count = 182 ACPI_GET16(target) - ACPI_GET16(source); 183 } else { 184 /* No vendor data to worry about */ 185 186 item_count = aml->large_header.resource_length + 187 sizeof(struct aml_resource_large_header) - 188 ACPI_GET16(source); 189 } 190 191 resource->length = resource->length + item_count; 192 ACPI_SET16(destination, item_count); 193 break; 194 195 case ACPI_RSC_COUNT_SERIAL_VEN: 196 197 item_count = ACPI_GET16(source) - info->value; 198 199 resource->length = resource->length + item_count; 200 ACPI_SET16(destination, item_count); 201 break; 202 203 case ACPI_RSC_COUNT_SERIAL_RES: 204 205 item_count = (aml_resource_length + 206 sizeof(struct aml_resource_large_header)) 207 - ACPI_GET16(source) - info->value; 208 209 resource->length = resource->length + item_count; 210 ACPI_SET16(destination, item_count); 211 break; 212 213 case ACPI_RSC_LENGTH: 214 215 resource->length = resource->length + info->value; 216 break; 217 218 case ACPI_RSC_MOVE8: 219 case ACPI_RSC_MOVE16: 220 case ACPI_RSC_MOVE32: 221 case ACPI_RSC_MOVE64: 222 /* 223 * Raw data move. Use the Info value field unless item_count has 224 * been previously initialized via a COUNT opcode 225 */ 226 if (info->value) { 227 item_count = info->value; 228 } 229 acpi_rs_move_data(destination, source, item_count, 230 info->opcode); 231 break; 232 233 case ACPI_RSC_MOVE_GPIO_PIN: 234 235 /* Generate and set the PIN data pointer */ 236 237 target = (char *)ACPI_ADD_PTR(void, resource, 238 (resource->length - 239 item_count * 2)); 240 *(u16 **)destination = ACPI_CAST_PTR(u16, target); 241 242 /* Copy the PIN data */ 243 244 source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); 245 acpi_rs_move_data(target, source, item_count, 246 info->opcode); 247 break; 248 249 case ACPI_RSC_MOVE_GPIO_RES: 250 251 /* Generate and set the resource_source string pointer */ 252 253 target = (char *)ACPI_ADD_PTR(void, resource, 254 (resource->length - 255 item_count)); 256 *(u8 **)destination = ACPI_CAST_PTR(u8, target); 257 258 /* Copy the resource_source string */ 259 260 source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); 261 acpi_rs_move_data(target, source, item_count, 262 info->opcode); 263 break; 264 265 case ACPI_RSC_MOVE_SERIAL_VEN: 266 267 /* Generate and set the Vendor Data pointer */ 268 269 target = (char *)ACPI_ADD_PTR(void, resource, 270 (resource->length - 271 item_count)); 272 *(u8 **)destination = ACPI_CAST_PTR(u8, target); 273 274 /* Copy the Vendor Data */ 275 276 source = ACPI_ADD_PTR(void, aml, info->value); 277 acpi_rs_move_data(target, source, item_count, 278 info->opcode); 279 break; 280 281 case ACPI_RSC_MOVE_SERIAL_RES: 282 283 /* Generate and set the resource_source string pointer */ 284 285 target = (char *)ACPI_ADD_PTR(void, resource, 286 (resource->length - 287 item_count)); 288 *(u8 **)destination = ACPI_CAST_PTR(u8, target); 289 290 /* Copy the resource_source string */ 291 292 source = 293 ACPI_ADD_PTR(void, aml, 294 (ACPI_GET16(source) + info->value)); 295 acpi_rs_move_data(target, source, item_count, 296 info->opcode); 297 break; 298 299 case ACPI_RSC_SET8: 300 301 memset(destination, info->aml_offset, info->value); 302 break; 303 304 case ACPI_RSC_DATA8: 305 306 target = ACPI_ADD_PTR(char, resource, info->value); 307 memcpy(destination, source, ACPI_GET16(target)); 308 break; 309 310 case ACPI_RSC_ADDRESS: 311 /* 312 * Common handler for address descriptor flags 313 */ 314 if (!acpi_rs_get_address_common(resource, aml)) { 315 return_ACPI_STATUS 316 (AE_AML_INVALID_RESOURCE_TYPE); 317 } 318 break; 319 320 case ACPI_RSC_SOURCE: 321 /* 322 * Optional resource_source (Index and String) 323 */ 324 resource->length += 325 acpi_rs_get_resource_source(aml_resource_length, 326 info->value, 327 destination, aml, NULL); 328 break; 329 330 case ACPI_RSC_SOURCEX: 331 /* 332 * Optional resource_source (Index and String). This is the more 333 * complicated case used by the Interrupt() macro 334 */ 335 target = ACPI_ADD_PTR(char, resource, 336 info->aml_offset + 337 (item_count * 4)); 338 339 resource->length += 340 acpi_rs_get_resource_source(aml_resource_length, 341 (acpi_rs_length) 342 (((item_count - 343 1) * sizeof(u32)) + 344 info->value), 345 destination, aml, 346 target); 347 break; 348 349 case ACPI_RSC_BITMASK: 350 /* 351 * 8-bit encoded bitmask (DMA macro) 352 */ 353 item_count = 354 acpi_rs_decode_bitmask(ACPI_GET8(source), 355 destination); 356 if (item_count) { 357 resource->length += (item_count - 1); 358 } 359 360 target = ACPI_ADD_PTR(char, resource, info->value); 361 ACPI_SET8(target, item_count); 362 break; 363 364 case ACPI_RSC_BITMASK16: 365 /* 366 * 16-bit encoded bitmask (IRQ macro) 367 */ 368 ACPI_MOVE_16_TO_16(&temp16, source); 369 370 item_count = 371 acpi_rs_decode_bitmask(temp16, destination); 372 if (item_count) { 373 resource->length += (item_count - 1); 374 } 375 376 target = ACPI_ADD_PTR(char, resource, info->value); 377 ACPI_SET8(target, item_count); 378 break; 379 380 case ACPI_RSC_EXIT_NE: 381 /* 382 * control - Exit conversion if not equal 383 */ 384 switch (info->resource_offset) { 385 case ACPI_RSC_COMPARE_AML_LENGTH: 386 387 if (aml_resource_length != info->value) { 388 goto exit; 389 } 390 break; 391 392 case ACPI_RSC_COMPARE_VALUE: 393 394 if (ACPI_GET8(source) != info->value) { 395 goto exit; 396 } 397 break; 398 399 default: 400 401 ACPI_ERROR((AE_INFO, 402 "Invalid conversion sub-opcode")); 403 return_ACPI_STATUS(AE_BAD_PARAMETER); 404 } 405 break; 406 407 default: 408 409 ACPI_ERROR((AE_INFO, "Invalid conversion opcode")); 410 return_ACPI_STATUS(AE_BAD_PARAMETER); 411 } 412 413 count--; 414 info++; 415 } 416 417 exit: 418 if (!flags_mode) { 419 420 /* Round the resource struct length up to the next boundary (32 or 64) */ 421 422 resource->length = (u32) 423 ACPI_ROUND_UP_TO_NATIVE_WORD(resource->length); 424 } 425 return_ACPI_STATUS(AE_OK); 426 } 427 428 /******************************************************************************* 429 * 430 * FUNCTION: acpi_rs_convert_resource_to_aml 431 * 432 * PARAMETERS: resource - Pointer to the resource descriptor 433 * aml - Where the AML descriptor is returned 434 * info - Pointer to appropriate conversion table 435 * 436 * RETURN: Status 437 * 438 * DESCRIPTION: Convert an internal resource descriptor to the corresponding 439 * external AML resource descriptor. 440 * 441 ******************************************************************************/ 442 443 acpi_status 444 acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, 445 union aml_resource *aml, 446 struct acpi_rsconvert_info *info) 447 { 448 void *source = NULL; 449 void *destination; 450 char *target; 451 acpi_rsdesc_size aml_length = 0; 452 u8 count; 453 u16 temp16 = 0; 454 u16 item_count = 0; 455 456 ACPI_FUNCTION_TRACE(rs_convert_resource_to_aml); 457 458 if (!info) { 459 return_ACPI_STATUS(AE_BAD_PARAMETER); 460 } 461 462 /* 463 * First table entry must be ACPI_RSC_INITxxx and must contain the 464 * table length (# of table entries) 465 */ 466 count = INIT_TABLE_LENGTH(info); 467 468 while (count) { 469 /* 470 * Source is the internal resource descriptor, 471 * destination is the external AML byte stream buffer 472 */ 473 source = ACPI_ADD_PTR(void, resource, info->resource_offset); 474 destination = ACPI_ADD_PTR(void, aml, info->aml_offset); 475 476 switch (info->opcode) { 477 case ACPI_RSC_INITSET: 478 479 memset(aml, 0, INIT_RESOURCE_LENGTH(info)); 480 aml_length = INIT_RESOURCE_LENGTH(info); 481 acpi_rs_set_resource_header(INIT_RESOURCE_TYPE(info), 482 aml_length, aml); 483 break; 484 485 case ACPI_RSC_INITGET: 486 break; 487 488 case ACPI_RSC_FLAGINIT: 489 /* 490 * Clear the flag byte 491 */ 492 ACPI_SET8(destination, 0); 493 break; 494 495 case ACPI_RSC_1BITFLAG: 496 /* 497 * Mask and shift the flag bit 498 */ 499 ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) 500 ((ACPI_GET8(source) & 0x01) << info-> 501 value)); 502 break; 503 504 case ACPI_RSC_2BITFLAG: 505 /* 506 * Mask and shift the flag bits 507 */ 508 ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) 509 ((ACPI_GET8(source) & 0x03) << info-> 510 value)); 511 break; 512 513 case ACPI_RSC_3BITFLAG: 514 /* 515 * Mask and shift the flag bits 516 */ 517 ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) 518 ((ACPI_GET8(source) & 0x07) << info-> 519 value)); 520 break; 521 522 case ACPI_RSC_6BITFLAG: 523 /* 524 * Mask and shift the flag bits 525 */ 526 ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) 527 ((ACPI_GET8(source) & 0x3F) << info-> 528 value)); 529 break; 530 531 case ACPI_RSC_COUNT: 532 533 item_count = ACPI_GET8(source); 534 ACPI_SET8(destination, item_count); 535 536 aml_length = (u16) 537 (aml_length + (info->value * (item_count - 1))); 538 break; 539 540 case ACPI_RSC_COUNT16: 541 542 item_count = ACPI_GET16(source); 543 aml_length = (u16) (aml_length + item_count); 544 acpi_rs_set_resource_length(aml_length, aml); 545 break; 546 547 case ACPI_RSC_COUNT_GPIO_PIN: 548 549 item_count = ACPI_GET16(source); 550 ACPI_SET16(destination, aml_length); 551 552 aml_length = (u16)(aml_length + item_count * 2); 553 target = ACPI_ADD_PTR(void, aml, info->value); 554 ACPI_SET16(target, aml_length); 555 acpi_rs_set_resource_length(aml_length, aml); 556 break; 557 558 case ACPI_RSC_COUNT_GPIO_VEN: 559 560 item_count = ACPI_GET16(source); 561 ACPI_SET16(destination, item_count); 562 563 aml_length = 564 (u16)(aml_length + (info->value * item_count)); 565 acpi_rs_set_resource_length(aml_length, aml); 566 break; 567 568 case ACPI_RSC_COUNT_GPIO_RES: 569 570 /* Set resource source string length */ 571 572 item_count = ACPI_GET16(source); 573 ACPI_SET16(destination, aml_length); 574 575 /* Compute offset for the Vendor Data */ 576 577 aml_length = (u16)(aml_length + item_count); 578 target = ACPI_ADD_PTR(void, aml, info->value); 579 580 /* Set vendor offset only if there is vendor data */ 581 582 ACPI_SET16(target, aml_length); 583 584 acpi_rs_set_resource_length(aml_length, aml); 585 break; 586 587 case ACPI_RSC_COUNT_SERIAL_VEN: 588 589 item_count = ACPI_GET16(source); 590 ACPI_SET16(destination, item_count + info->value); 591 aml_length = (u16)(aml_length + item_count); 592 acpi_rs_set_resource_length(aml_length, aml); 593 break; 594 595 case ACPI_RSC_COUNT_SERIAL_RES: 596 597 item_count = ACPI_GET16(source); 598 aml_length = (u16)(aml_length + item_count); 599 acpi_rs_set_resource_length(aml_length, aml); 600 break; 601 602 case ACPI_RSC_LENGTH: 603 604 acpi_rs_set_resource_length(info->value, aml); 605 break; 606 607 case ACPI_RSC_MOVE8: 608 case ACPI_RSC_MOVE16: 609 case ACPI_RSC_MOVE32: 610 case ACPI_RSC_MOVE64: 611 612 if (info->value) { 613 item_count = info->value; 614 } 615 acpi_rs_move_data(destination, source, item_count, 616 info->opcode); 617 break; 618 619 case ACPI_RSC_MOVE_GPIO_PIN: 620 621 destination = (char *)ACPI_ADD_PTR(void, aml, 622 ACPI_GET16 623 (destination)); 624 source = *(u16 **)source; 625 acpi_rs_move_data(destination, source, item_count, 626 info->opcode); 627 break; 628 629 case ACPI_RSC_MOVE_GPIO_RES: 630 631 /* Used for both resource_source string and vendor_data */ 632 633 destination = (char *)ACPI_ADD_PTR(void, aml, 634 ACPI_GET16 635 (destination)); 636 source = *(u8 **)source; 637 acpi_rs_move_data(destination, source, item_count, 638 info->opcode); 639 break; 640 641 case ACPI_RSC_MOVE_SERIAL_VEN: 642 643 destination = (char *)ACPI_ADD_PTR(void, aml, 644 (aml_length - 645 item_count)); 646 source = *(u8 **)source; 647 acpi_rs_move_data(destination, source, item_count, 648 info->opcode); 649 break; 650 651 case ACPI_RSC_MOVE_SERIAL_RES: 652 653 destination = (char *)ACPI_ADD_PTR(void, aml, 654 (aml_length - 655 item_count)); 656 source = *(u8 **)source; 657 acpi_rs_move_data(destination, source, item_count, 658 info->opcode); 659 break; 660 661 case ACPI_RSC_ADDRESS: 662 663 /* Set the Resource Type, General Flags, and Type-Specific Flags */ 664 665 acpi_rs_set_address_common(aml, resource); 666 break; 667 668 case ACPI_RSC_SOURCEX: 669 /* 670 * Optional resource_source (Index and String) 671 */ 672 aml_length = 673 acpi_rs_set_resource_source(aml, 674 (acpi_rs_length) 675 aml_length, source); 676 acpi_rs_set_resource_length(aml_length, aml); 677 break; 678 679 case ACPI_RSC_SOURCE: 680 /* 681 * Optional resource_source (Index and String). This is the more 682 * complicated case used by the Interrupt() macro 683 */ 684 aml_length = 685 acpi_rs_set_resource_source(aml, info->value, 686 source); 687 acpi_rs_set_resource_length(aml_length, aml); 688 break; 689 690 case ACPI_RSC_BITMASK: 691 /* 692 * 8-bit encoded bitmask (DMA macro) 693 */ 694 ACPI_SET8(destination, 695 acpi_rs_encode_bitmask(source, 696 *ACPI_ADD_PTR(u8, 697 resource, 698 info-> 699 value))); 700 break; 701 702 case ACPI_RSC_BITMASK16: 703 /* 704 * 16-bit encoded bitmask (IRQ macro) 705 */ 706 temp16 = 707 acpi_rs_encode_bitmask(source, 708 *ACPI_ADD_PTR(u8, resource, 709 info->value)); 710 ACPI_MOVE_16_TO_16(destination, &temp16); 711 break; 712 713 case ACPI_RSC_EXIT_LE: 714 /* 715 * control - Exit conversion if less than or equal 716 */ 717 if (item_count <= info->value) { 718 goto exit; 719 } 720 break; 721 722 case ACPI_RSC_EXIT_NE: 723 /* 724 * control - Exit conversion if not equal 725 */ 726 switch (COMPARE_OPCODE(info)) { 727 case ACPI_RSC_COMPARE_VALUE: 728 729 if (*ACPI_ADD_PTR(u8, resource, 730 COMPARE_TARGET(info)) != 731 COMPARE_VALUE(info)) { 732 goto exit; 733 } 734 break; 735 736 default: 737 738 ACPI_ERROR((AE_INFO, 739 "Invalid conversion sub-opcode")); 740 return_ACPI_STATUS(AE_BAD_PARAMETER); 741 } 742 break; 743 744 case ACPI_RSC_EXIT_EQ: 745 /* 746 * control - Exit conversion if equal 747 */ 748 if (*ACPI_ADD_PTR(u8, resource, 749 COMPARE_TARGET(info)) == 750 COMPARE_VALUE(info)) { 751 goto exit; 752 } 753 break; 754 755 default: 756 757 ACPI_ERROR((AE_INFO, "Invalid conversion opcode")); 758 return_ACPI_STATUS(AE_BAD_PARAMETER); 759 } 760 761 count--; 762 info++; 763 } 764 765 exit: 766 return_ACPI_STATUS(AE_OK); 767 } 768 769 #if 0 770 /* Previous resource validations */ 771 772 if (aml->ext_address64.revision_ID != AML_RESOURCE_EXTENDED_ADDRESS_REVISION) { 773 return_ACPI_STATUS(AE_SUPPORT); 774 } 775 776 if (resource->data.start_dpf.performance_robustness >= 3) { 777 return_ACPI_STATUS(AE_AML_BAD_RESOURCE_VALUE); 778 } 779 780 if (((aml->irq.flags & 0x09) == 0x00) || ((aml->irq.flags & 0x09) == 0x09)) { 781 /* 782 * Only [active_high, edge_sensitive] or [active_low, level_sensitive] 783 * polarity/trigger interrupts are allowed (ACPI spec, section 784 * "IRQ Format"), so 0x00 and 0x09 are illegal. 785 */ 786 ACPI_ERROR((AE_INFO, 787 "Invalid interrupt polarity/trigger in resource list, 0x%X", 788 aml->irq.flags)); 789 return_ACPI_STATUS(AE_BAD_DATA); 790 } 791 792 resource->data.extended_irq.interrupt_count = temp8; 793 if (temp8 < 1) { 794 795 /* Must have at least one IRQ */ 796 797 return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); 798 } 799 800 if (resource->data.dma.transfer == 0x03) { 801 ACPI_ERROR((AE_INFO, "Invalid DMA.Transfer preference (3)")); 802 return_ACPI_STATUS(AE_BAD_DATA); 803 } 804 #endif 805