core/recovery/ocp_recovery_device.c (467 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "ocp_recovery_device.h"
#include "common/buffer_util.h"
#include "common/common_math.h"
#include "common/unused.h"
/**
* Placeholder for the active command code when no command has been started.
*/
#define OCP_RECOVERY_DEVICE_NO_COMMAND 0
/**
* The maximum number of bytes that can be read from an indirect memory region while maintaining
* 4-byte alignment.
*/
#define OCP_RECOVERY_DEVICE_MAX_INDIRECT_READ 252
/**
* Initialize a device handler for the OCP Recovery protocol.
*
* @param device The handler to initialize.
* @param state The recovery state to initialize. This must not already be initialized.
* @param hw The HW interface for executing device actions in response to recovery commands.
* @param cms_list A list of memory regions that are accessible through the recovery handler. This
* can be null if there are no supported regions.
* @param cms_count The number of memory regions in the list.
*
* @return 0 if the handler was successfully initialized or an error code.
*/
int ocp_recovery_device_init (struct ocp_recovery_device *device,
struct ocp_recovery_device_state *state, const struct ocp_recovery_device_hw *hw,
const struct ocp_recovery_device_cms *cms_list, size_t cms_count)
{
if ((device == NULL) || (state == NULL) || (hw == NULL) ||
((cms_list == NULL) && (cms_count != 0))) {
return OCP_RECOVERY_DEVICE_INVALID_ARGUMENT;
}
memset (device, 0, sizeof (struct ocp_recovery_device));
device->state = state;
device->hw = hw;
device->cms = cms_list;
device->cms_count = cms_count;
return ocp_recovery_device_init_state (device);
}
/**
* Initialize only the variable state of an OCP Recovery handler. The rest of a device handler
* structure is assumed to have already been initialized.
*
* This would generally be used with a statically initialized handler instance.
*
* @param device The recovery handler containing the state to initialize.
*
* @return 0 if the handler state was successfully initialized or an error code.
*/
int ocp_recovery_device_init_state (const struct ocp_recovery_device *device)
{
size_t i;
if ((device == NULL) || (device->state == NULL)) {
return OCP_RECOVERY_DEVICE_INVALID_ARGUMENT;
}
memset (device->state, 0, sizeof (struct ocp_recovery_device_state));
if (!device->hw->activate_recovery || !device->cms) {
device->state->recovery_status = OCP_RECOVERY_RECOVERY_STATUS_NOT_RECOVERY_MODE;
}
else {
device->state->recovery_status = OCP_RECOVERY_RECOVERY_STATUS_WAITING_FOR_IMAGE;
}
for (i = 0; i < device->cms_count; i++) {
switch (device->cms[i].type & ~OCP_RECOVERY_INDIRECT_STATUS_REGION_POLLING_FLAG) {
case OCP_RECOVERY_INDIRECT_STATUS_REGION_RECOVERY_CODE:
case OCP_RECOVERY_INDIRECT_STATUS_REGION_VENDOR_RW:
if (device->cms[i].length & 0x3) {
/* Writable regions must by 4-byte aligned. */
return OCP_RECOVERY_DEVICE_RW_CMS_NOT_ALIGNED;
}
if (device->cms[i].length == OCP_RECOVERY_DEVICE_CMS_LENGTH_VARIABLE) {
/* Writable regions can't use logging interfaces. */
return OCP_RECOVERY_DEVICE_RW_LOG;
}
break;
default:
/* Other region types can handle mis-alignment, though it is not ideal. */
break;
}
}
return 0;
}
/**
* Release the resources for an OCP Recovery device handler. This will release both the handler
* instance and the associated state.
*
* @param device The handler to release.
*/
void ocp_recovery_device_release (const struct ocp_recovery_device *device)
{
UNUSED (device);
}
/**
* Notify the device handler that a new recovery command has been received. This must be called as
* soon as the command code is known. The final direction of the command is not known at this
* point.
*
* If the command is invalid, the request must be NACKed.
*
* @param device The recovery handler to update.
* @param command_code The received command code.
*
* @return 0 if the command has been accepted by the handler or an error code. If the command is
* invalid, OCP_RECOVERY_DEVICE_NACK is returned to indicate the transaction must be NACKed.
*/
int ocp_recovery_device_start_new_command (const struct ocp_recovery_device *device,
uint8_t command_code)
{
if (device == NULL) {
return OCP_RECOVERY_DEVICE_INVALID_ARGUMENT;
}
if ((command_code < OCP_RECOVERY_CMD_MIN_VALID) ||
(command_code > OCP_RECOVERY_CMD_MAX_VALID)) {
device->state->protocol_status |= OCP_RECOVERY_DEVICE_STATUS_PROTO_UNSUPPORTED_CMD;
return OCP_RECOVERY_DEVICE_NACK;
}
device->state->active_cmd = command_code;
return 0;
}
/**
* Process a received RESET recovery command.
*
* @param device The recovery handler that will process the command.
* @param reset Buffer containing the request.
*
* @return 0 if the a device reset was triggered or OCP_RECOVERY_DEVICE_UNSUPPORTED if reset is not
* supported. In most cases, 0 will never be returned because the device would have been reset.
*/
static int ocp_recovery_device_write_reset (const struct ocp_recovery_device *device,
const struct ocp_recovery_reset *reset)
{
int status = 0;
if (reset->intf_control != OCP_RECOVERY_RESET_INTF_DISABLE_MASTERING) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED_PARAM;
}
if ((!device->hw->supports_forced_recovery) &&
(reset->forced_recovery == OCP_RECOVERY_RESET_FORCED_RECOVERY)) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED_PARAM;
}
switch (reset->reset_ctrl) {
case OCP_RECOVERY_RESET_DEVICE_RESET:
if (device->hw->reset_device) {
device->hw->reset_device (device->hw,
(reset->forced_recovery == OCP_RECOVERY_RESET_FORCED_RECOVERY));
}
else {
status = OCP_RECOVERY_DEVICE_UNSUPPORTED_PARAM;
}
break;
case OCP_RECOVERY_RESET_MGMT_RESET:
if (device->hw->reset_management) {
device->hw->reset_management (device->hw,
(reset->forced_recovery == OCP_RECOVERY_RESET_FORCED_RECOVERY));
}
else {
status = OCP_RECOVERY_DEVICE_UNSUPPORTED_PARAM;
}
break;
}
/* If the device resets, this won't get called, but if the device is not reset, store the
* value that was written. */
if (status == 0) {
memcpy (&device->state->reset, reset, sizeof (struct ocp_recovery_reset));
device->state->reset.reset_ctrl = OCP_RECOVERY_RESET_NO_RESET;
}
return status;
}
/**
* Process a received RECOVERY_CTRL recovery command.
*
* @param device The recovery handler that will process the command.
* @param recovery_ctrl Buffer containing the request.
*
* @return 0 if the request completed successfully or an error code.
*/
static int ocp_recovery_device_write_recovery_ctrl (const struct ocp_recovery_device *device,
const struct ocp_recovery_recovery_ctrl *recovery_ctrl)
{
bool auth_error;
int status = 0;
if (recovery_ctrl->cms >= device->cms_count) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED_CMS;
}
if (recovery_ctrl->recovery_image == OCP_RECOVERY_RECOVERY_CTRL_IMAGE_IN_DEVICE) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED_PARAM;
}
if ((!device->hw->activate_recovery) &&
((recovery_ctrl->recovery_image == OCP_RECOVERY_RECOVERY_CTRL_IMAGE_FROM_CMS) ||
(recovery_ctrl->activate == OCP_RECOVERY_RECOVERY_CTRL_ACTIVATE_IMAGE))) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED_PARAM;
}
if (recovery_ctrl->recovery_image == OCP_RECOVERY_RECOVERY_CTRL_IMAGE_FROM_CMS) {
if (device->cms[recovery_ctrl->cms].type !=
OCP_RECOVERY_INDIRECT_STATUS_REGION_RECOVERY_CODE) {
device->state->recovery_status = OCP_RECOVERY_RECOVERY_STATUS_INVALID_CMS;
status = OCP_RECOVERY_DEVICE_CMS_NOT_CODE_REGION;
}
if ((status == 0) &&
(recovery_ctrl->activate == OCP_RECOVERY_RECOVERY_CTRL_ACTIVATE_IMAGE)) {
status = device->hw->activate_recovery (device->hw, &device->cms[recovery_ctrl->cms],
&auth_error);
if (status == 0) {
device->state->recovery_status = OCP_RECOVERY_RECOVERY_STATUS_SUCCESSFUL;
}
else {
if (auth_error) {
device->state->recovery_status = OCP_RECOVERY_RECOVERY_STATUS_AUTH_FAILURE;
}
else {
device->state->recovery_status = OCP_RECOVERY_RECOVERY_STATUS_FAILED;
}
}
}
}
memcpy (&device->state->recovery_ctrl, recovery_ctrl,
sizeof (struct ocp_recovery_recovery_ctrl));
device->state->recovery_ctrl.activate = OCP_RECOVERY_RECOVERY_CTRL_ACTIVATE_NONE;
return status;
}
/**
* Process a received INDIRECT_CTRL recovery command.
*
* @param device The recovery handler that will process the command.
* @param indirect_ctrl Buffer containing the request.
*
* @return 0 if the request completed successfully or an error code.
*/
static int ocp_recovery_device_write_indirect_ctrl (const struct ocp_recovery_device *device,
const struct ocp_recovery_indirect_ctrl *indirect_ctrl)
{
if (!device->cms) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED;
}
/* There doesn't need to be any validation done on the values at this point. If the CMS is not
* supported, it will be reported as such by INDIRECT_STATUS and INDIRECT_DATA will not allow
* any access. If the offset is too large for the region, it will get corrected prior to any
* INDIRECT_DATA access. */
memcpy (&device->state->indirect_ctrl, indirect_ctrl,
sizeof (struct ocp_recovery_indirect_ctrl));
/* Address offset must be 4-byte aligned. Move to the next aligned address. */
device->state->indirect_ctrl.offset = (device->state->indirect_ctrl.offset + 3) & ~0x3ull;
return 0;
}
/**
* Process a received INDIRECT_DATA recovery command.
*
* @param device The recovery handler that will process the command.
* @param indirect_data Buffer containing the request.
* @param length The amount of data being written.
*
* @return 0 if the request completed successfully or an error code.
*/
static int ocp_recovery_device_write_indirect_data (const struct ocp_recovery_device *device,
const struct ocp_recovery_indirect_data *indirect_data, size_t length)
{
const struct ocp_recovery_device_cms *cms;
size_t write_len;
const uint8_t *pos;
if (!device->cms) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED;
}
/* Find the CMS that is currently being accessed. */
if (device->state->indirect_ctrl.cms >= device->cms_count) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED_CMS;
}
cms = &device->cms[device->state->indirect_ctrl.cms];
if ((cms->type != OCP_RECOVERY_INDIRECT_STATUS_REGION_RECOVERY_CODE) &&
(cms->type != OCP_RECOVERY_INDIRECT_STATUS_REGION_VENDOR_RW)) {
device->state->indirect_status |= OCP_RECOVERY_INDIRECT_STATUS_READ_ONLY;
return OCP_RECOVERY_DEVICE_RO_CMS;
}
/* Copy the data to the memory region. */
pos = indirect_data->data;
do {
/* Check the offset to see if the read should wrap to the beginning. */
if (device->state->indirect_ctrl.offset >= cms->length) {
device->state->indirect_ctrl.offset = 0;
device->state->indirect_status |= OCP_RECOVERY_INDIRECT_STATUS_OVERLFLOW;
}
write_len = min (length, cms->length - device->state->indirect_ctrl.offset);
memcpy (&cms->base_addr[device->state->indirect_ctrl.offset], pos, write_len);
/* Make sure we only ever increment the offset in 4-byte chunks. */
device->state->indirect_ctrl.offset += (write_len + 3) & ~0x3ull;
length -= write_len;
pos += write_len;
} while (length > 0);
return 0;
}
/**
* The recovery command is writing data to the device. This call is only valid after first calling
* ocp_recovery_device_start_new_command.
*
* Once this function returns, the current command context is closed and the handler begins waiting
* for a new command.
*
* @param device The recovery handler that will process the command.
* @param data The command data received from the requestor. This must only be the recovery command
* data. Any bus protocol data must not be included.
* @param length The number of data bytes received.
*
* @return 0 if the command was processed successfully or an error code.
*/
int ocp_recovery_device_write_request (const struct ocp_recovery_device *device,
const union ocp_recovery_device_cmd_buffer *data, size_t length)
{
int status = OCP_RECOVERY_DEVICE_RO_COMMAND;
if ((device == NULL) || (data == NULL)) {
return OCP_RECOVERY_DEVICE_INVALID_ARGUMENT;
}
switch (device->state->active_cmd) {
case OCP_RECOVERY_DEVICE_NO_COMMAND:
status = OCP_RECOVERY_DEVICE_NO_ACTIVE_COMMAND;
break;
case OCP_RECOVERY_CMD_RESET:
if (length == sizeof (struct ocp_recovery_reset)) {
status = ocp_recovery_device_write_reset (device, &data->reset);
}
else if (length > sizeof (struct ocp_recovery_reset)) {
status = OCP_RECOVERY_DEVICE_EXTRA_CMD_BYTES;
}
else {
status = OCP_RECOVERY_DEVICE_CMD_INCOMPLETE;
}
break;
case OCP_RECOVERY_CMD_RECOVERY_CTRL:
if (length == sizeof (struct ocp_recovery_recovery_ctrl)) {
status = ocp_recovery_device_write_recovery_ctrl (device, &data->recovery_ctrl);
}
else if (length > sizeof (struct ocp_recovery_recovery_ctrl)) {
status = OCP_RECOVERY_DEVICE_EXTRA_CMD_BYTES;
}
else {
status = OCP_RECOVERY_DEVICE_CMD_INCOMPLETE;
}
break;
case OCP_RECOVERY_CMD_INDIRECT_CTRL:
if (length == sizeof (struct ocp_recovery_indirect_ctrl)) {
status = ocp_recovery_device_write_indirect_ctrl (device, &data->indirect_ctrl);
}
else if (length > sizeof (struct ocp_recovery_indirect_ctrl)) {
status = OCP_RECOVERY_DEVICE_EXTRA_CMD_BYTES;
}
else {
status = OCP_RECOVERY_DEVICE_CMD_INCOMPLETE;
}
break;
case OCP_RECOVERY_CMD_INDIRECT_DATA:
status = ocp_recovery_device_write_indirect_data (device, &data->indirect_data, length);
break;
case OCP_RECOVERY_CMD_VENDOR:
status = OCP_RECOVERY_DEVICE_UNSUPPORTED;
break;
}
/* Update the protocol status in the case of an error. */
switch (status) {
case 0:
/* Successful command. */
break;
case OCP_RECOVERY_DEVICE_UNSUPPORTED:
case OCP_RECOVERY_DEVICE_RO_COMMAND:
device->state->protocol_status = OCP_RECOVERY_DEVICE_STATUS_PROTO_UNSUPPORTED_CMD;
break;
case OCP_RECOVERY_DEVICE_UNSUPPORTED_PARAM:
case OCP_RECOVERY_DEVICE_UNSUPPORTED_CMS:
device->state->protocol_status = OCP_RECOVERY_DEVICE_STATUS_PROTO_UNSUPPORTED_PARAM;
break;
case OCP_RECOVERY_DEVICE_CMD_INCOMPLETE:
case OCP_RECOVERY_DEVICE_EXTRA_CMD_BYTES:
device->state->protocol_status = OCP_RECOVERY_DEVICE_STATUS_PROTO_LENGTH_ERROR;
break;
}
device->state->active_cmd = OCP_RECOVERY_DEVICE_NO_COMMAND;
return status;
}
/**
* Read the data to respond to a PROT_CAP recovery command.
*
* @param device The recovery handler that will process the command.
* @param prot_cap Output buffer for the command data.
*
* @return The number of bytes in the command response or an error code.
*/
static int ocp_recovery_device_read_prot_cap (const struct ocp_recovery_device *device,
struct ocp_recovery_prot_cap *prot_cap)
{
memcpy (prot_cap->magic_string, OCP_RECOVERY_PROT_CAP_MAGIC_STRING,
sizeof (prot_cap->magic_string));
prot_cap->major_version = OCP_RECOVERY_PROT_CAP_MAJOR_VERSION;
prot_cap->minor_version = OCP_RECOVERY_PROT_CAP_MINOR_VERSION;
prot_cap->capabilities = OCP_RECOVERY_PROT_CAP_SUPPORTS_IDENTIFICATION |
OCP_RECOVERY_PROT_CAP_SUPPORTS_DEVICE_STATUS;
prot_cap->cms_regions = 0;
prot_cap->max_response_time = 0x10; // This translates to a 65ms response time.
prot_cap->heartbeat_period = 0; // Heartbeat is not supported.
if (device->hw->reset_device) {
prot_cap->capabilities |= OCP_RECOVERY_PROT_CAP_SUPPORTS_DEVICE_RESET;
}
if (device->hw->reset_management) {
prot_cap->capabilities |= OCP_RECOVERY_PROT_CAP_SUPPORTS_MGMT_RESET;
}
if (device->hw->supports_forced_recovery) {
prot_cap->capabilities |= OCP_RECOVERY_PROT_CAP_SUPPORTS_FORCED_RECOVERY;
}
if (device->cms) {
prot_cap->capabilities |= OCP_RECOVERY_PROT_CAP_SUPPORTS_MEMORY_ACCESS;
prot_cap->cms_regions = device->cms_count;
if (device->hw->activate_recovery) {
prot_cap->capabilities |= OCP_RECOVERY_PROT_CAP_SUPPORTS_PUSH_IMAGE;
}
}
return sizeof (struct ocp_recovery_prot_cap);
}
/**
* Read the data to respond to a DEVICE_STATUS recovery command.
*
* @param device The recovery handler that will process the command.
* @param device_status Output buffer for the command data.
*
* @return The number of bytes in the command response or an error code.
*/
static int ocp_recovery_device_read_device_status (const struct ocp_recovery_device *device,
struct ocp_recovery_device_status *device_status)
{
enum ocp_recovery_device_status_code status_code;
enum ocp_recovery_recovery_reason_code reason_code;
device->hw->get_device_status (device->hw, &status_code, &reason_code,
(struct ocp_recovery_device_status_vendor*) device_status->vendor_status);
device_status->base.status = status_code;
device_status->base.protocol_status = device->state->protocol_status;
device_status->base.recovery_reason = reason_code;
device_status->base.heartbeat = 0;
device_status->base.vendor_length = sizeof (struct ocp_recovery_device_status_vendor);
/* Clear the protocol status on read. */
device->state->protocol_status = 0;
return sizeof (device_status->base) + device_status->base.vendor_length;
}
/**
* Read the data to respond to an INDIRECT_STATUS recovery command.
*
* @param device The recovery handler that will process the command.
* @param indirect_status Output buffer for the command data.
*
* @return The number of bytes in the command response or an error code.
*/
static int ocp_recovery_device_read_indirect_status (const struct ocp_recovery_device *device,
struct ocp_recovery_indirect_status *indirect_status)
{
const struct ocp_recovery_device_cms *cms;
int status;
if (!device->cms) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED;
}
indirect_status->status = device->state->indirect_status;
if (device->state->indirect_ctrl.cms < device->cms_count) {
cms = &device->cms[device->state->indirect_ctrl.cms];
indirect_status->type = cms->type;
if (cms->length != OCP_RECOVERY_DEVICE_CMS_LENGTH_VARIABLE) {
indirect_status->size = cms->length;
}
else {
status = cms->variable->get_size (cms->variable);
if (ROT_IS_ERROR (status)) {
return status;
}
indirect_status->size = status;
}
/* Make sure the size gets rounded up to the next 4-byte unit. */
indirect_status->size = (indirect_status->size + 3) / 4;
}
else {
indirect_status->type = OCP_RECOVERY_INDIRECT_STATUS_REGION_UNSUPPORTED;
indirect_status->size = 0;
}
/* Clear the indirect status on read. */
device->state->indirect_status = 0;
return sizeof (struct ocp_recovery_indirect_status);
}
/**
* Read the data to respond to an INDIRECT_DATA recovery command.
*
* @param device The recovery handler that will process the command.
* @param indirect_status Output buffer for the command data.
*
* @return The number of bytes in the command response or an error code.
*/
static int ocp_recovery_device_read_indirect_data (const struct ocp_recovery_device *device,
struct ocp_recovery_indirect_data *indirect_data)
{
const struct ocp_recovery_device_cms *cms;
size_t read_len;
if (!device->cms) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED;
}
/* Find the CMS that is currently being accessed. */
if (device->state->indirect_ctrl.cms >= device->cms_count) {
return OCP_RECOVERY_DEVICE_UNSUPPORTED_CMS;
}
cms = &device->cms[device->state->indirect_ctrl.cms];
/* Check the offset to see if the read should wrap to the beginning. */
if (cms->length == OCP_RECOVERY_DEVICE_CMS_LENGTH_VARIABLE) {
read_len = cms->variable->get_size (cms->variable);
if (ROT_IS_ERROR ((int) read_len)) {
return read_len;
}
}
else {
read_len = cms->length;
}
if (device->state->indirect_ctrl.offset >= read_len) {
device->state->indirect_ctrl.offset = 0;
device->state->indirect_status |= OCP_RECOVERY_INDIRECT_STATUS_OVERLFLOW;
}
/* Read the data from the memory region. */
memset (indirect_data->data, 0, sizeof (indirect_data->data));
if (cms->length == OCP_RECOVERY_DEVICE_CMS_LENGTH_VARIABLE) {
read_len = cms->variable->get_data (cms->variable, device->state->indirect_ctrl.offset,
indirect_data->data, OCP_RECOVERY_DEVICE_MAX_INDIRECT_READ);
if (ROT_IS_ERROR ((int) read_len)) {
return read_len;
}
}
else {
size_t max_length = OCP_RECOVERY_DEVICE_MAX_INDIRECT_READ;
size_t offset = device->state->indirect_ctrl.offset;
read_len = buffer_copy (cms->base_addr, cms->length, &offset, &max_length,
indirect_data->data);
}
/* Make sure we only ever increment the offset in 4-byte chunks. */
read_len = (read_len + 3) & ~0x3ull;
device->state->indirect_ctrl.offset += read_len;
return read_len;
}
/**
* The recovery command is requesting data from device. This call is only valid after first calling
* ocp_recovery_device_start_new_command.
*
* Once this function returns, the current command context is closed and the handler begins waiting
* for a new command.
*
* @param device The recovery handler that will process the command.
* @param data Output buffer for the requested data. This will only contain the recovery command
* data. Any bus protocol data must not be included.
*
* @return The number of bytes written to the data buffer or an error code. Use ROT_IS_ERROR to
* check the return value.
*/
int ocp_recovery_device_read_request (const struct ocp_recovery_device *device,
union ocp_recovery_device_cmd_buffer *data)
{
int status;
if ((device == NULL) || (data == NULL)) {
return OCP_RECOVERY_DEVICE_INVALID_ARGUMENT;
}
switch (device->state->active_cmd) {
case OCP_RECOVERY_DEVICE_NO_COMMAND:
return OCP_RECOVERY_DEVICE_NO_ACTIVE_COMMAND;
case OCP_RECOVERY_CMD_PROT_CAP:
status = ocp_recovery_device_read_prot_cap (device, &data->prot_cap);
break;
case OCP_RECOVERY_CMD_DEVICE_ID:
status = device->hw->get_device_id (device->hw, &data->device_id);
break;
case OCP_RECOVERY_CMD_DEVICE_STATUS:
status = ocp_recovery_device_read_device_status (device, &data->device_status);
break;
case OCP_RECOVERY_CMD_RESET:
status = sizeof (struct ocp_recovery_reset);
memcpy (&data->reset, &device->state->reset, status);
break;
case OCP_RECOVERY_CMD_RECOVERY_CTRL:
status = sizeof (struct ocp_recovery_recovery_ctrl);
memcpy (&data->recovery_ctrl, &device->state->recovery_ctrl, status);
break;
case OCP_RECOVERY_CMD_RECOVERY_STATUS:
data->recovery_status.status = device->state->recovery_status;
data->recovery_status.vendor_status = 0;
status = sizeof (struct ocp_recovery_recovery_status);
break;
case OCP_RECOVERY_CMD_INDIRECT_CTRL:
if (device->cms) {
status = sizeof (struct ocp_recovery_indirect_ctrl);
memcpy (&data->indirect_ctrl, &device->state->indirect_ctrl, status);
}
else {
status = OCP_RECOVERY_DEVICE_UNSUPPORTED;
}
break;
case OCP_RECOVERY_CMD_INDIRECT_STATUS:
status = ocp_recovery_device_read_indirect_status (device, &data->indirect_status);
break;
case OCP_RECOVERY_CMD_INDIRECT_DATA:
status = ocp_recovery_device_read_indirect_data (device, &data->indirect_data);
break;
default:
status = OCP_RECOVERY_DEVICE_UNSUPPORTED;
break;
}
/* Update the protocol status in the case of an error. */
switch (status) {
case 0:
/* Successful command. */
break;
case OCP_RECOVERY_DEVICE_UNSUPPORTED:
device->state->protocol_status = OCP_RECOVERY_DEVICE_STATUS_PROTO_UNSUPPORTED_CMD;
break;
case OCP_RECOVERY_DEVICE_UNSUPPORTED_CMS:
device->state->protocol_status = OCP_RECOVERY_DEVICE_STATUS_PROTO_UNSUPPORTED_PARAM;
break;
}
device->state->active_cmd = OCP_RECOVERY_DEVICE_NO_COMMAND;
return status;
}
/**
* Notify the recovery handler of a checksum failure at the physical layer. Any current command
* context must be closed, since that command was corrupted during transmission.
*
* @param device The recovery handler to update.
*/
void ocp_recovery_device_checksum_failure (const struct ocp_recovery_device *device)
{
if (device) {
device->state->active_cmd = OCP_RECOVERY_DEVICE_NO_COMMAND;
device->state->protocol_status = OCP_RECOVERY_DEVICE_STATUS_PROTO_CRC_ERROR;
}
}
/**
* Notify the recovery handler that the physical layer has received more data than allowed by the
* protocol, overflowing the command buffer. The received command has been discarded and any
* current command context should be closed.
*
* @param device The recovery handler to update.
*/
void ocp_recovery_device_write_overflow (const struct ocp_recovery_device *device)
{
if (device) {
device->state->active_cmd = OCP_RECOVERY_DEVICE_NO_COMMAND;
device->state->protocol_status = OCP_RECOVERY_DEVICE_STATUS_PROTO_LENGTH_ERROR;
}
}
/**
* Notify the recovery handler that the physical layer has received less data then specified by the
* command. The received command has been discarded and any current command context should be
* closed.
*
* @param device The recovery handler to update.
*/
void ocp_recovery_device_write_incomplete (const struct ocp_recovery_device *device)
{
/* This has the same behavior as the overflow case. */
ocp_recovery_device_write_overflow (device);
}