acpi_status acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, void *buffer, u32 buffer_length) { acpi_status status; u64 raw_datum; u64 merged_datum; u32 field_offset = 0; u32 buffer_offset = 0; u32 buffer_tail_bits; u32 datum_count; u32 field_datum_count; u32 access_bit_width; u32 i; ACPI_FUNCTION_TRACE(ex_extract_from_field); /* Validate target buffer and clear it */ if (buffer_length < ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) { ACPI_ERROR((AE_INFO, "Field size %u (bits) is too large for buffer (%u)", obj_desc->common_field.bit_length, buffer_length)); return_ACPI_STATUS(AE_BUFFER_OVERFLOW); } ACPI_MEMSET(buffer, 0, buffer_length); access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width); /* Handle the simple case here */ if ((obj_desc->common_field.start_field_bit_offset == 0) && (obj_desc->common_field.bit_length == access_bit_width)) { status = acpi_ex_field_datum_io(obj_desc, 0, buffer, ACPI_READ); return_ACPI_STATUS(status); } /* TBD: Move to common setup code */ /* Field algorithm is limited to sizeof(u64), truncate if needed */ if (obj_desc->common_field.access_byte_width > sizeof(u64)) { obj_desc->common_field.access_byte_width = sizeof(u64); access_bit_width = sizeof(u64) * 8; } /* Compute the number of datums (access width data items) */ datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, access_bit_width); field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length + obj_desc->common_field. start_field_bit_offset, access_bit_width); /* Priming read from the field */ status = acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum, ACPI_READ); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; /* Read the rest of the field */ for (i = 1; i < field_datum_count; i++) { /* Get next input datum from the field */ field_offset += obj_desc->common_field.access_byte_width; status = acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum, ACPI_READ); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } /* * Merge with previous datum if necessary. * * Note: Before the shift, check if the shift value will be larger than * the integer size. If so, there is no need to perform the operation. * This avoids the differences in behavior between different compilers * concerning shift values larger than the target data width. */ if (access_bit_width - obj_desc->common_field.start_field_bit_offset < ACPI_INTEGER_BIT_SIZE) { merged_datum |= raw_datum << (access_bit_width - obj_desc->common_field. start_field_bit_offset); } if (i == datum_count) { break; } /* Write merged datum to target buffer */ ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum, ACPI_MIN(obj_desc->common_field.access_byte_width, buffer_length - buffer_offset)); buffer_offset += obj_desc->common_field.access_byte_width; merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; } /* Mask off any extra bits in the last datum */ buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width; if (buffer_tail_bits) { merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits); } /* Write the last datum to the buffer */ ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum, ACPI_MIN(obj_desc->common_field.access_byte_width, buffer_length - buffer_offset)); return_ACPI_STATUS(AE_OK); }
acpi_status acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc, u64 mask, u64 field_value, u32 field_datum_byte_offset) { acpi_status status = AE_OK; u64 merged_value; u64 current_value; ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask); /* Start with the new bits */ merged_value = field_value; /* If the mask is all ones, we don't need to worry about the update rule */ if (mask != ACPI_UINT64_MAX) { /* Decode the update rule */ switch (obj_desc->common_field. field_flags & AML_FIELD_UPDATE_RULE_MASK) { case AML_FIELD_UPDATE_PRESERVE: /* * Check if update rule needs to be applied (not if mask is all * ones) The left shift drops the bits we want to ignore. */ if ((~mask << (ACPI_MUL_8(sizeof(mask)) - ACPI_MUL_8(obj_desc->common_field. access_byte_width))) != 0) { /* * Read the current contents of the byte/word/dword containing * the field, and merge with the new field value. */ status = acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset, ¤t_value, ACPI_READ); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } merged_value |= (current_value & ~mask); } break; case AML_FIELD_UPDATE_WRITE_AS_ONES: /* Set positions outside the field to all ones */ merged_value |= ~mask; break; case AML_FIELD_UPDATE_WRITE_AS_ZEROS: /* Set positions outside the field to all zeros */ merged_value &= mask; break; default: ACPI_ERROR((AE_INFO, "Unknown UpdateRule value: 0x%X", (obj_desc->common_field. field_flags & AML_FIELD_UPDATE_RULE_MASK))); return_ACPI_STATUS(AE_AML_OPERAND_VALUE); } } ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n", ACPI_FORMAT_UINT64(mask), field_datum_byte_offset, obj_desc->common_field.access_byte_width, ACPI_FORMAT_UINT64(field_value), ACPI_FORMAT_UINT64(merged_value))); /* Write the merged value */ status = acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset, &merged_value, ACPI_WRITE); return_ACPI_STATUS(status); }
acpi_status acpi_ex_extract_from_field ( union acpi_operand_object *obj_desc, void *buffer, u32 buffer_length) { acpi_status status; acpi_integer raw_datum; acpi_integer merged_datum; u32 field_offset = 0; u32 buffer_offset = 0; u32 buffer_tail_bits; u32 datum_count; u32 field_datum_count; u32 i; ACPI_FUNCTION_TRACE ("ex_extract_from_field"); /* Validate target buffer and clear it */ if (buffer_length < ACPI_ROUND_BITS_UP_TO_BYTES ( obj_desc->common_field.bit_length)) { ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Field size %X (bits) is too large for buffer (%X)\n", obj_desc->common_field.bit_length, buffer_length)); return_ACPI_STATUS (AE_BUFFER_OVERFLOW); } ACPI_MEMSET (buffer, 0, buffer_length); /* Compute the number of datums (access width data items) */ datum_count = ACPI_ROUND_UP_TO ( obj_desc->common_field.bit_length, obj_desc->common_field.access_bit_width); field_datum_count = ACPI_ROUND_UP_TO ( obj_desc->common_field.bit_length + obj_desc->common_field.start_field_bit_offset, obj_desc->common_field.access_bit_width); /* Priming read from the field */ status = acpi_ex_field_datum_io (obj_desc, field_offset, &raw_datum, ACPI_READ); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; /* Read the rest of the field */ for (i = 1; i < field_datum_count; i++) { /* Get next input datum from the field */ field_offset += obj_desc->common_field.access_byte_width; status = acpi_ex_field_datum_io (obj_desc, field_offset, &raw_datum, ACPI_READ); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } /* Merge with previous datum if necessary */ merged_datum |= raw_datum << (obj_desc->common_field.access_bit_width - obj_desc->common_field.start_field_bit_offset); if (i == datum_count) { break; } /* Write merged datum to target buffer */ ACPI_MEMCPY (((char *) buffer) + buffer_offset, &merged_datum, ACPI_MIN(obj_desc->common_field.access_byte_width, buffer_length - buffer_offset)); buffer_offset += obj_desc->common_field.access_byte_width; merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; } /* Mask off any extra bits in the last datum */ buffer_tail_bits = obj_desc->common_field.bit_length % obj_desc->common_field.access_bit_width; if (buffer_tail_bits) { merged_datum &= ACPI_MASK_BITS_ABOVE (buffer_tail_bits); } /* Write the last datum to the buffer */ ACPI_MEMCPY (((char *) buffer) + buffer_offset, &merged_datum, ACPI_MIN(obj_desc->common_field.access_byte_width, buffer_length - buffer_offset)); return_ACPI_STATUS (AE_OK); }
acpi_status acpi_ex_extract_from_field ( union acpi_operand_object *obj_desc, void *buffer, u32 buffer_length) { acpi_status status; u32 field_datum_byte_offset; u32 datum_offset; acpi_integer previous_raw_datum; acpi_integer this_raw_datum = 0; acpi_integer merged_datum = 0; u32 byte_field_length; u32 datum_count; ACPI_FUNCTION_TRACE ("ex_extract_from_field"); /* * The field must fit within the caller's buffer */ byte_field_length = ACPI_ROUND_BITS_UP_TO_BYTES (obj_desc->common_field.bit_length); if (byte_field_length > buffer_length) { ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Field size %X (bytes) too large for buffer (%X)\n", byte_field_length, buffer_length)); return_ACPI_STATUS (AE_BUFFER_OVERFLOW); } /* Convert field byte count to datum count, round up if necessary */ datum_count = ACPI_ROUND_UP_TO (byte_field_length, obj_desc->common_field.access_byte_width); ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "byte_len %X, datum_len %X, byte_gran %X\n", byte_field_length, datum_count,obj_desc->common_field.access_byte_width)); /* * Clear the caller's buffer (the whole buffer length as given) * This is very important, especially in the cases where a byte is read, * but the buffer is really a u32 (4 bytes). */ ACPI_MEMSET (buffer, 0, buffer_length); /* Read the first raw datum to prime the loop */ field_datum_byte_offset = 0; datum_offset= 0; status = acpi_ex_field_datum_io (obj_desc, field_datum_byte_offset, &previous_raw_datum, ACPI_READ); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } /* We might actually be done if the request fits in one datum */ if ((datum_count == 1) && (obj_desc->common_field.flags & AOPOBJ_SINGLE_DATUM)) { /* 1) Shift the valid data bits down to start at bit 0 */ merged_datum = (previous_raw_datum >> obj_desc->common_field.start_field_bit_offset); /* 2) Mask off any upper unused bits (bits not part of the field) */ if (obj_desc->common_field.end_buffer_valid_bits) { merged_datum &= ACPI_MASK_BITS_ABOVE (obj_desc->common_field.end_buffer_valid_bits); } /* Store the datum to the caller buffer */ acpi_ex_set_buffer_datum (merged_datum, buffer, buffer_length, obj_desc->common_field.access_byte_width, datum_offset); return_ACPI_STATUS (AE_OK); }
acpi_status acpi_ex_extract_from_field ( union acpi_operand_object *obj_desc, void *buffer, u32 buffer_length) { acpi_status status; u32 field_datum_byte_offset; u32 buffer_datum_offset; acpi_integer previous_raw_datum = 0; acpi_integer this_raw_datum = 0; acpi_integer merged_datum = 0; u32 byte_field_length; u32 datum_count; u32 i; ACPI_FUNCTION_TRACE ("ex_extract_from_field"); /* * The field must fit within the caller's buffer */ byte_field_length = ACPI_ROUND_BITS_UP_TO_BYTES (obj_desc->common_field.bit_length); if (byte_field_length > buffer_length) { ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Field size %X (bytes) too large for buffer (%X)\n", byte_field_length, buffer_length)); return_ACPI_STATUS (AE_BUFFER_OVERFLOW); } /* Convert field byte count to datum count, round up if necessary */ datum_count = ACPI_ROUND_UP_TO (byte_field_length, obj_desc->common_field.access_byte_width); /* * If the field is not aligned on a datum boundary and does not * fit within a single datum, we must read an extra datum. * * We could just split the aligned and non-aligned cases since the * aligned case is so very simple, but this would require more code. */ if ((obj_desc->common_field.end_field_valid_bits != 0) && (!(obj_desc->common_field.flags & AOPOBJ_SINGLE_DATUM))) { datum_count++; } ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "byte_len %X, datum_len %X, byte_gran %X\n", byte_field_length, datum_count,obj_desc->common_field.access_byte_width)); /* * Clear the caller's buffer (the whole buffer length as given) * This is very important, especially in the cases where the buffer * is longer than the size of the field. */ ACPI_MEMSET (buffer, 0, buffer_length); field_datum_byte_offset = 0; buffer_datum_offset= 0; /* Read the entire field */ for (i = 0; i < datum_count; i++) { status = acpi_ex_field_datum_io (obj_desc, field_datum_byte_offset, &this_raw_datum, ACPI_READ); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } /* We might actually be done if the request fits in one datum */ if ((datum_count == 1) && (obj_desc->common_field.flags & AOPOBJ_SINGLE_DATUM)) { /* 1) Shift the valid data bits down to start at bit 0 */ merged_datum = (this_raw_datum >> obj_desc->common_field.start_field_bit_offset); /* 2) Mask off any upper unused bits (bits not part of the field) */ if (obj_desc->common_field.end_buffer_valid_bits) { merged_datum &= ACPI_MASK_BITS_ABOVE (obj_desc->common_field.end_buffer_valid_bits); } /* Store the datum to the caller buffer */ acpi_ex_set_buffer_datum (merged_datum, buffer, buffer_length, obj_desc->common_field.access_byte_width, buffer_datum_offset); return_ACPI_STATUS (AE_OK); } /* Special handling for the last datum to ignore extra bits */ if ((i >= (datum_count -1)) && (obj_desc->common_field.end_field_valid_bits)) { /* * This is the last iteration of the loop. We need to clear * any unused bits (bits that are not part of this field) before * we store the final merged datum into the caller buffer. */ this_raw_datum &= ACPI_MASK_BITS_ABOVE (obj_desc->common_field.end_field_valid_bits); } /* * Create the (possibly) merged datum to be stored to the caller buffer */ if (obj_desc->common_field.start_field_bit_offset == 0) { /* Field is not skewed and we can just copy the datum */ acpi_ex_set_buffer_datum (this_raw_datum, buffer, buffer_length, obj_desc->common_field.access_byte_width, buffer_datum_offset); buffer_datum_offset++; } else { /* Not aligned -- on the first iteration, just save the datum */ if (i != 0) { /* * Put together the appropriate bits of the two raw data to make a * single complete field datum * * 1) Normalize the first datum down to bit 0 */ merged_datum = (previous_raw_datum >> obj_desc->common_field.start_field_bit_offset); /* 2) Insert the second datum "above" the first datum */ merged_datum |= (this_raw_datum << obj_desc->common_field.datum_valid_bits); acpi_ex_set_buffer_datum (merged_datum, buffer, buffer_length, obj_desc->common_field.access_byte_width, buffer_datum_offset); buffer_datum_offset++; } /* * Save the raw datum that was just acquired since it may contain bits * of the *next* field datum */ previous_raw_datum = this_raw_datum; }