mirror of
https://github.com/google/pebble.git
synced 2025-05-22 03:14:52 +00:00
208 lines
6.9 KiB
C
208 lines
6.9 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "kernel/system_message.h"
|
|
|
|
#include "flash_region/filesystem_regions.h"
|
|
#include "kernel/events.h"
|
|
#include "kernel/util/sleep.h"
|
|
#include "process_management/worker_manager.h"
|
|
#include "services/common/comm_session/protocol.h"
|
|
#include "services/common/comm_session/session.h"
|
|
#include "services/common/firmware_update.h"
|
|
#include "services/common/i18n/i18n.h"
|
|
#include "services/common/put_bytes/put_bytes.h"
|
|
#include "services/common/system_task.h"
|
|
#include "services/runlevel.h"
|
|
#include "system/bootbits.h"
|
|
#include "system/logging.h"
|
|
#include "system/passert.h"
|
|
#include "system/reboot_reason.h"
|
|
#include "system/reset.h"
|
|
#include "system/version.h"
|
|
#include "util/attributes.h"
|
|
#include "util/net.h"
|
|
|
|
#include "FreeRTOS.h"
|
|
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <inttypes.h>
|
|
|
|
static const uint16_t ENDPOINT_ID = 0x12;
|
|
|
|
void system_message_send(SystemMessageType type) {
|
|
uint8_t buffer[2] = { 0x00, type };
|
|
CommSession *system_session = comm_session_get_system_session();
|
|
comm_session_send_data(system_session, ENDPOINT_ID, buffer, sizeof(buffer), COMM_SESSION_DEFAULT_TIMEOUT);
|
|
PBL_LOG(LOG_LEVEL_DEBUG, "Sending sysmsg: %u", type);
|
|
}
|
|
|
|
static void prv_reset_kernel_bg_cb(void *unused) {
|
|
PBL_LOG(LOG_LEVEL_ALWAYS, "Rebooting to install firmware...");
|
|
RebootReason reason = { RebootReasonCode_SoftwareUpdate, 0 };
|
|
reboot_reason_set(&reason);
|
|
system_reset();
|
|
}
|
|
|
|
static void prv_ui_update_reset_delay_timer_callback(void *unused) {
|
|
system_task_add_callback(prv_reset_kernel_bg_cb, NULL);
|
|
}
|
|
|
|
static void prv_handle_firmware_complete_msg(void) {
|
|
uint32_t timeout = 3000;
|
|
|
|
// Wait 3 seconds before rebooting so there is time to show the update complete screen
|
|
PBL_LOG(LOG_LEVEL_ALWAYS, "Delaying reset by 3s so the UI can update...");
|
|
TimerID timer = new_timer_create(); // Don't bother cleaning up this timer, we're going to reset
|
|
PBL_ASSERTN(timer != TIMER_INVALID_ID);
|
|
new_timer_start(timer, timeout, prv_ui_update_reset_delay_timer_callback, NULL, 0);
|
|
}
|
|
|
|
//! Note: For now we just call into storage directly for the status of FW installs. Someday,
|
|
//! it would be nice for this exchange to take place as part of PutBytes
|
|
extern bool pb_storage_get_status(PutBytesObjectType obj_type, PbInstallStatus *status);
|
|
static void prv_handle_firmware_status_request(CommSession *session) {
|
|
struct PACKED {
|
|
uint8_t deprecated;
|
|
uint8_t type;
|
|
uint8_t rsvd[2];
|
|
uint32_t resource_bytes_written;
|
|
uint32_t resource_crc;
|
|
uint32_t firmware_bytes_written;
|
|
uint32_t firmware_crc;
|
|
} fw_status_resp = {
|
|
.type = SysMsgFirmwareStatusResponse,
|
|
};
|
|
|
|
PbInstallStatus status = { };
|
|
if (pb_storage_get_status(ObjectFirmware, &status)) {
|
|
fw_status_resp.firmware_bytes_written = status.num_bytes_written;
|
|
fw_status_resp.firmware_crc = status.crc_of_bytes;
|
|
}
|
|
|
|
if (pb_storage_get_status(ObjectSysResources, &status)) {
|
|
fw_status_resp.resource_bytes_written = status.num_bytes_written;
|
|
fw_status_resp.resource_crc = status.crc_of_bytes;
|
|
}
|
|
|
|
PBL_LOG(LOG_LEVEL_INFO, "FW Status Resp: res %"PRIu32" : 0x%x fw %"PRIu32" : 0x%x",
|
|
fw_status_resp.resource_bytes_written, (int)fw_status_resp.resource_crc,
|
|
fw_status_resp.firmware_bytes_written, (int)fw_status_resp.firmware_crc);
|
|
|
|
comm_session_send_data(session, ENDPOINT_ID, (uint8_t *)&fw_status_resp, sizeof(fw_status_resp),
|
|
COMM_SESSION_DEFAULT_TIMEOUT);
|
|
}
|
|
|
|
void sys_msg_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) {
|
|
PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_KernelBackground);
|
|
|
|
SystemMessageType t = data[1];
|
|
|
|
PBL_LOG(LOG_LEVEL_DEBUG, "Received sysmsg: %u", t);
|
|
|
|
switch (t) {
|
|
case SysMsgFirmwareAvailable_Deprecated: {
|
|
PBL_LOG(LOG_LEVEL_DEBUG, "Deprecated available message received.");
|
|
break;
|
|
}
|
|
|
|
case SysMsgFirmwareStart: {
|
|
PBL_LOG_VERBOSE("About to receive new firmware!");
|
|
|
|
uint32_t bytes_transferred = 0;
|
|
uint32_t total_size = 0;
|
|
|
|
bool smooth_progress_supported =
|
|
comm_session_has_capability(session, CommSessionSmoothFwInstallProgressSupport) &&
|
|
(length >= sizeof(SysMsgSmoothFirmwareStartPayload));
|
|
|
|
if (smooth_progress_supported) {
|
|
SysMsgSmoothFirmwareStartPayload *payload = (SysMsgSmoothFirmwareStartPayload *)data;
|
|
bytes_transferred = payload->bytes_already_transferred;
|
|
total_size = bytes_transferred + payload->bytes_to_transfer;
|
|
PBL_LOG(LOG_LEVEL_INFO, "Starting FW update, %"PRIu32" of %"PRIu32" bytes already "
|
|
"transferred", bytes_transferred, total_size);
|
|
}
|
|
|
|
PebbleEvent e = {
|
|
.type = PEBBLE_SYSTEM_MESSAGE_EVENT,
|
|
.firmware_update = {
|
|
.type = smooth_progress_supported ?
|
|
PebbleSystemMessageFirmwareUpdateStart : PebbleSystemMessageFirmwareUpdateStartLegacy,
|
|
.bytes_transferred = bytes_transferred,
|
|
.total_transfer_size = total_size,
|
|
}
|
|
};
|
|
event_put(&e);
|
|
break;
|
|
}
|
|
|
|
case SysMsgFirmwareStatus:
|
|
prv_handle_firmware_status_request(session);
|
|
break;
|
|
|
|
case SysMsgFirmwareComplete: {
|
|
PBL_LOG_VERBOSE("Firmware transfer succeeded, okay to restart!");
|
|
PebbleEvent e = {
|
|
.type = PEBBLE_SYSTEM_MESSAGE_EVENT,
|
|
.firmware_update.type = PebbleSystemMessageFirmwareUpdateComplete,
|
|
};
|
|
event_put(&e);
|
|
prv_handle_firmware_complete_msg();
|
|
break;
|
|
}
|
|
|
|
case SysMsgFirmwareFail: {
|
|
PBL_LOG_VERBOSE("Firmware transfer failed, time to clean up!");
|
|
PebbleEvent e = {
|
|
.type = PEBBLE_SYSTEM_MESSAGE_EVENT,
|
|
.firmware_update.type = PebbleSystemMessageFirmwareUpdateFailed,
|
|
};
|
|
event_put(&e);
|
|
break;
|
|
}
|
|
|
|
case SysMsgFirmwareUpToDate: {
|
|
PBL_LOG_VERBOSE("Firmware is up to date!");
|
|
PebbleEvent e = {
|
|
.type = PEBBLE_SYSTEM_MESSAGE_EVENT,
|
|
.firmware_update.type = PebbleSystemMessageFirmwareUpToDate,
|
|
};
|
|
event_put(&e);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
PBL_LOG(LOG_LEVEL_ERROR, "Invalid message received, type is %u", data[1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void system_message_send_firmware_start_response(FirmwareUpdateStatus status) {
|
|
struct PACKED {
|
|
uint8_t zero;
|
|
uint8_t type;
|
|
uint8_t status;
|
|
} msg = {
|
|
.zero = 0x00,
|
|
.type = SysMsgFirmwareStartResponse,
|
|
.status = status
|
|
};
|
|
|
|
CommSession *session = comm_session_get_system_session();
|
|
comm_session_send_data(session, ENDPOINT_ID, (const uint8_t*) &msg, sizeof(msg), COMM_SESSION_DEFAULT_TIMEOUT);
|
|
}
|