int flash_store_contiguous_blocks_write_common()

in core/flash/flash_store_contiguous_blocks.c [64:251]


int flash_store_contiguous_blocks_write_common (const struct flash_store_contiguous_blocks *flash,
	int id, const uint8_t *data, size_t length, const uint8_t *extra_data, size_t extra_length)
{
	int base_offset;
	int offset;
	int status;

	base_offset = id * flash->state->block_size;
	if (flash->decreasing) {
		base_offset = -base_offset;
	}
	offset = base_offset;

	status = flash_sector_erase_region (flash->flash, flash->base_addr + base_offset,
		flash->state->block_size);
	if (status != 0) {
		return status;
	}

#ifdef FLASH_STORE_SUPPORT_NO_PARTIAL_PAGE_WRITE
	if (flash->state->page_buffer) {
		/* It is necessary to ensure that no page is written more than once.  Internal buffering is
		 * necessary in three cases:
		 * 1. The first page of data for variable length storage, since that holds the data header.
		 * 2. The last page of data for storage that appends extra data to the end, but only in
		 * cases where the stored data does not align to page boundaries.
		 * 3. The entire amount of data (header, data, and extra data) fits into the first page.
		 *
		 * Data that is naturally aligned to full pages is not buffered and is written directly from
		 * the source data. */
		struct flash_store_header header = {
			.header_len = FLASH_STORE_HEADER_LENGTH,
			.marker = FLASH_STORE_HEADER_MARKER,
			.length = length
		};
		size_t header_len = (!flash->variable) ? 0 :
				(flash->state->old_header) ? sizeof (header.length) : sizeof (header);
		size_t first = flash->state->page_size - header_len;
		size_t remain = FLASH_REGION_OFFSET (length + header_len, flash->state->page_size);
		size_t write_extra = flash->state->page_size - remain;
		size_t middle = (length > remain) ? (length - remain) : length;
		bool extra_first = false;

		if (flash->variable) {
			if (middle > first) {
				middle -= first;
			}
			else {
				if (middle < first) {
					/* All the data fits into the first flash page. */
					extra_first = true;
				}
				first = middle;
				middle = 0;
			}
		}
		else {
			if (middle < first) {
				middle = 0;
			}
			first = 0;
		}

		if (extra_length == 0) {
			if (!extra_first) {
				middle += remain;
			}
			remain = 0;
			write_extra = 0;
		}
		else if (write_extra > extra_length) {
			write_extra = extra_length;
		}

		/* Write full pages of source data. */
		offset += first + header_len;

		if (middle != 0) {
			status = flash_write_and_verify (flash->flash, flash->base_addr + offset, &data[first],
				middle);
			if (status != 0) {
				return status;
			}

			offset += middle;
		}

		/* Write the additional data appended to the end. */
		if (!extra_first && (extra_length != 0)) {
			platform_mutex_lock (&flash->state->lock);
			memcpy (flash->state->page_buffer, &data[length - remain], remain);
			memcpy (&flash->state->page_buffer[remain], extra_data, write_extra);

			status = flash_write_and_verify (flash->flash, flash->base_addr + offset,
				flash->state->page_buffer, remain + write_extra);
			platform_mutex_unlock (&flash->state->lock);
			if (status != 0) {
				return status;
			}

			offset += remain + write_extra;
		}
		else {
			offset += write_extra;
		}

		if (write_extra < extra_length) {
			status = flash_write_and_verify (flash->flash, flash->base_addr + offset,
				&extra_data[write_extra], extra_length - write_extra);
			if (status != 0) {
				return status;
			}
		}

		/* For variable storage, write the first page with the data header. */
		if (first != 0) {
			platform_mutex_lock (&flash->state->lock);
			if (!flash->state->old_header) {
				memcpy (flash->state->page_buffer, (uint8_t*) &header, sizeof (header));
			}
			else {
				memcpy (flash->state->page_buffer, (uint8_t*) &header.length,
					sizeof (header.length));
			}
			memcpy (&flash->state->page_buffer[header_len], data, first);
			if (extra_first) {
				memcpy (&flash->state->page_buffer[header_len + first], extra_data, write_extra);
				header_len += write_extra;
			}

			status = flash_write_and_verify (flash->flash, flash->base_addr + base_offset,
				flash->state->page_buffer, first + header_len);
			platform_mutex_unlock (&flash->state->lock);
			if (status != 0) {
				return status;
			}
		}
	}
	else
#endif
	{
		/* Each page can be written multiple times without erasing. */
		if (flash->variable) {
			if (!flash->state->old_header) {
				offset += FLASH_STORE_HEADER_LENGTH;
			}
			else {
				offset += sizeof (uint16_t);
			}
		}

		status = flash_write_and_verify (flash->flash, flash->base_addr + offset, data, length);
		if (status != 0) {
			return status;
		}

		if (extra_data) {
			offset += length;
			status = flash_write_and_verify (flash->flash, flash->base_addr + offset, extra_data,
				extra_length);
			if (status != 0) {
				return status;
			}
		}

		if (flash->variable) {
			struct flash_store_header header = {
				.header_len = FLASH_STORE_HEADER_LENGTH,
				.marker = FLASH_STORE_HEADER_MARKER,
				.length = length
			};

			if (!flash->state->old_header) {
				status = flash_write_and_verify (flash->flash, flash->base_addr + base_offset,
					(uint8_t*) &header, sizeof (header));
			}
			else {
				status = flash_write_and_verify (flash->flash, flash->base_addr + base_offset,
					(uint8_t*) &header.length, sizeof (header.length));
			}
			if (status != 0) {
				return status;
			}
		}
	}

	return 0;
}