Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7a7f754
Fix charge fail issue of hotplug dock on same port (#595)
zhuyi2024 Dec 2, 2025
2eaac0f
Merge branch 'v0.2.0' into main
jerrysxie Dec 11, 2025
ca4c6c2
type-c-service: Depanic (#633)
RobertZ2011 Dec 12, 2025
555e20a
power-policy-service: Depanic (#634)
RobertZ2011 Dec 12, 2025
7f3db26
Remove unnecessary parameters unconstrained_power from Func cmp_consu…
WangYuanyuan1996 Dec 15, 2025
1a56d16
keyboard-service: fix syntax error in task macro (#640)
williampMSFT Dec 16, 2025
586030f
power-policy: Add PSU type to capabilities flags (#627)
RobertZ2011 Dec 18, 2025
8b13c1e
platform-service: minimize panic paths (#653)
jerrysxie Dec 19, 2025
653eb8d
hid-service: Depanic (#641)
RobertZ2011 Dec 19, 2025
1621263
battery-service: minimize panic code paths (#639)
jerrysxie Dec 20, 2025
bef9e3f
Ucsi battery charging status (#644)
RobertZ2011 Dec 20, 2025
abf6f0d
embedded-service cfu: audit panics (#656)
jerrysxie Dec 22, 2025
96b3d2e
embedded-service hid: audit panic paths (#658)
jerrysxie Dec 22, 2025
b136133
Audit panics path in embedded-service type-c (#657)
jerrysxie Dec 22, 2025
6046965
cfu-service: audit panic paths (#660)
jerrysxie Dec 23, 2025
72ea4e5
partition-mananger: audit panics (#655)
jerrysxie Dec 23, 2025
a9bec2b
Exclude MCTP from panic audit (#661)
jerrysxie Dec 23, 2025
4017ec7
Update embedded-usb-pd (#664)
kurtjd Dec 24, 2025
b195d23
buffer: Audit for panics and refactor (#662)
kurtjd Dec 24, 2025
cf625d6
Minimize panic path for power policy action (#665)
jerrysxie Dec 24, 2025
9cd6a97
Add panic lints (#663)
jerrysxie Jan 5, 2026
7545545
v0.2.0 mergeback: 01-05-2026
tullom Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ repository = "https://github.com/OpenDevicePartnership/embedded-services"
[workspace.lints.rust]
warnings = "deny"

[workspace.lints.clippy]
correctness = "deny"
expect_used = "deny"
indexing_slicing = "deny"
panic = "deny"
panic_in_result_fn = "deny"
perf = "deny"
suspicious = "deny"
style = "deny"
todo = "deny"
unimplemented = "deny"
unreachable = "deny"
unwrap_used = "deny"

[workspace.dependencies]
aligned = "0.4"
anyhow = "1.0"
Expand Down
55 changes: 29 additions & 26 deletions battery-service/src/acpi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,32 +134,35 @@ pub(crate) fn compute_bix<'a>(
oem_info: [0u8; STD_BIX_OEM_SIZE],
battery_swapping_capability: embedded_batteries_async::acpi::BatterySwapCapability::NonSwappable,
};
bix_return.model_number[..core::cmp::min(STD_BIX_MODEL_SIZE - 1, static_cache.device_name.len() - 1)]
.copy_from_slice(
static_cache.device_name[..core::cmp::min(STD_BIX_MODEL_SIZE - 1, static_cache.device_name.len() - 1)]
.try_into()
.map_err(|_| ())?,
);
bix_return.serial_number[..core::cmp::min(STD_BIX_SERIAL_SIZE - 1, static_cache.serial_num.len() - 1)]
.copy_from_slice(
static_cache.serial_num[..core::cmp::min(STD_BIX_SERIAL_SIZE - 1, static_cache.serial_num.len() - 1)]
.try_into()
.map_err(|_| ())?,
);
bix_return.battery_type[..core::cmp::min(STD_BIX_BATTERY_SIZE - 1, static_cache.device_chemistry.len() - 1)]
.copy_from_slice(
static_cache.device_chemistry
[..core::cmp::min(STD_BIX_BATTERY_SIZE - 1, static_cache.device_chemistry.len() - 1)]
.try_into()
.map_err(|_| ())?,
);
bix_return.oem_info[..core::cmp::min(STD_BIX_OEM_SIZE - 1, static_cache.manufacturer_name.len() - 1)]
.copy_from_slice(
static_cache.manufacturer_name
[..core::cmp::min(STD_BIX_OEM_SIZE - 1, static_cache.manufacturer_name.len() - 1)]
.try_into()
.map_err(|_| ())?,
);

let model_number_len = core::cmp::min(STD_BIX_MODEL_SIZE - 1, static_cache.device_name.len() - 1);
bix_return
.model_number
.get_mut(..model_number_len)
.ok_or(())?
.copy_from_slice(static_cache.device_name.get(..model_number_len).ok_or(())?);

let serial_number_len = core::cmp::min(STD_BIX_SERIAL_SIZE - 1, static_cache.serial_num.len() - 1);
bix_return
.serial_number
.get_mut(..serial_number_len)
.ok_or(())?
.copy_from_slice(static_cache.serial_num.get(..serial_number_len).ok_or(())?);

let battery_type_len = core::cmp::min(STD_BIX_BATTERY_SIZE - 1, static_cache.device_chemistry.len() - 1);
bix_return
.battery_type
.get_mut(..battery_type_len)
.ok_or(())?
.copy_from_slice(static_cache.device_chemistry.get(..battery_type_len).ok_or(())?);

let oem_info_len = core::cmp::min(STD_BIX_OEM_SIZE - 1, static_cache.manufacturer_name.len() - 1);
bix_return
.oem_info
.get_mut(..oem_info_len)
.ok_or(())?
.copy_from_slice(static_cache.manufacturer_name.get(..oem_info_len).ok_or(())?);

Ok(bix_return)
}

Expand Down
27 changes: 17 additions & 10 deletions battery-service/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,21 @@ impl Context {
},
Err(_) => {
error!("Battery state machine timeout!");
// Should be infallible
self.do_state_machine(BatteryEvent {
event: BatteryEventInner::Timeout,
device_id: event.device_id,
})
.await
.expect("Error type is Infallible");
self.battery_response.send(Err(ContextError::Timeout)).await;

match self
.do_state_machine(BatteryEvent {
event: BatteryEventInner::Timeout,
device_id: event.device_id,
})
.await
{
Ok(_) => {
self.battery_response.send(Err(ContextError::Timeout)).await;
}
Err(e) => {
self.battery_response.send(Err(ContextError::StateError(e))).await;
}
}
}
};
}
Expand Down Expand Up @@ -281,7 +288,7 @@ impl Context {
Ok(State::Present(PresentSubstate::NotOperational))
}
}
BatteryEventInner::Oem(_, _items) => todo!(),
BatteryEventInner::Oem(_, _items) => Err(StateMachineError::InvalidActionInState),
}
}

Expand Down Expand Up @@ -529,7 +536,7 @@ impl Context {
embedded_services::power::policy::CommsData::ConsumerConnected(_device_id, power_capability) => {
*psu_state = PsuState {
psu_connected: true,
power_capability: Some(*power_capability),
power_capability: Some(power_capability.capability),
}
}
_rest => { /* Don't care about anything else */ }
Expand Down
7 changes: 3 additions & 4 deletions cfu-service/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,10 @@ impl<'a> Buffer<'a> {
/// If the component is busy, this will wait indefinitely since the component will not be able to process
async fn wait_buffered_content(&self, is_busy: bool) -> FwUpdateContentCommand {
if is_busy {
let () = pending().await;
unreachable!();
} else {
self.buffer_receiver.receive().await
pending::<()>().await;
}

self.buffer_receiver.receive().await
}

/// Wait for an event
Expand Down
30 changes: 13 additions & 17 deletions cfu-service/src/splitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@ impl<'a, C: Customization> Splitter<'a, C> {
})
.await;

if !success {
self.create_invalid_fw_version_response()
} else {
let mut overall_version = self.customization.resolve_fw_versions(&versions[..self.devices.len()]);
if success && let Some(versions) = versions.get(..self.devices.len()) {
let mut overall_version = self.customization.resolve_fw_versions(versions);

// The overall component version comes first
overall_version.component_info[0].component_id = self.cfu_device.component_id();
InternalResponseData::FwVersionResponse(overall_version)
} else {
self.create_invalid_fw_version_response()
}
}

Expand All @@ -119,13 +120,10 @@ impl<'a, C: Customization> Splitter<'a, C> {
})
.await;

if !success {
self.create_invalid_fw_version_response()
if success && let Some(offer_responses_slice) = offer_responses.get(..self.devices.len()) {
InternalResponseData::OfferResponse(self.customization.resolve_offer_response(offer_responses_slice))
} else {
InternalResponseData::OfferResponse(
self.customization
.resolve_offer_response(&offer_responses[..self.devices.len()]),
)
self.create_invalid_fw_version_response()
}
}

Expand All @@ -145,13 +143,10 @@ impl<'a, C: Customization> Splitter<'a, C> {
})
.await;

if !success {
Self::create_content_rejection(content.header.sequence_num)
if success && let Some(content_responses_slice) = content_responses.get(..self.devices.len()) {
InternalResponseData::ContentResponse(self.customization.resolve_content_response(content_responses_slice))
} else {
InternalResponseData::ContentResponse(
self.customization
.resolve_content_response(&content_responses[..self.devices.len()]),
)
Self::create_content_rejection(content.header.sequence_num)
}
}

Expand Down Expand Up @@ -230,6 +225,8 @@ async fn map_slice_join<'i, 'o, I, O, F: Future<Output = Option<O>>>(
) -> bool {
let mut iter = zip(input.iter(), output.iter_mut());
loop {
// panic safety: other combinations aren't possible because we're using a fused iterator
#[allow(clippy::unreachable)]
match (iter.next(), iter.next(), iter.next(), iter.next()) {
(None, None, None, None) => {
// No more items to process
Expand Down Expand Up @@ -273,7 +270,6 @@ async fn map_slice_join<'i, 'o, I, O, F: Future<Output = Option<O>>>(
}
}
_ => {
// Other combinations aren't possible because we're using a fused iterator
unreachable!()
}
}
Expand Down
1 change: 1 addition & 0 deletions cfu-service/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::CfuClient;
pub async fn task() {
info!("Starting cfu client task");
static CLIENT: OnceLock<CfuClient> = OnceLock::new();
#[allow(clippy::expect_used)] // panic safety: singleton panic on initialization
let cfuclient = CLIENT.get_or_init(|| CfuClient::create().expect("cfu client singleton already initialized"));

if comms::register_endpoint(cfuclient, &cfuclient.tp).await.is_err() {
Expand Down
4 changes: 4 additions & 0 deletions debug-service/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#![no_std]
#![allow(clippy::expect_used)]
#![allow(clippy::indexing_slicing)]
#![allow(clippy::unwrap_used)]

mod debug_service;
mod defmt_ring_logger;
pub mod task;
Expand Down
17 changes: 11 additions & 6 deletions debug-service/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ use embedded_services::{

use crate::{debug_service_entry, defmt_ring_logger::DEFMT_BUFFER, frame_available, shared_buffer};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error {
Buffer(embedded_services::buffer::Error),
}

pub async fn debug_service(endpoint: comms::Endpoint) {
debug_service_entry(endpoint).await;
}

pub async fn defmt_to_host_task() {
pub async fn defmt_to_host_task() -> Result<embedded_services::Never, Error> {
embedded_services::info!("defmt to host task start");
use crate::debug_service::{host_endpoint_id, response_notify_signal};
use embedded_services::comms::{self, EndpointID, Internal};
Expand All @@ -34,7 +39,7 @@ pub async fn defmt_to_host_task() {
// destination length to be robust if the staging buffer size changes.
let copy_len = core::cmp::min(frame.len(), acpi_owned.len());
{
let mut access = acpi_owned.borrow_mut();
let mut access = acpi_owned.borrow_mut().map_err(Error::Buffer)?;
let buf: &mut [u8] = BorrowMut::borrow_mut(&mut access);

buf[..copy_len].copy_from_slice(&frame[..copy_len]);
Expand Down Expand Up @@ -69,7 +74,7 @@ pub async fn defmt_to_host_task() {
status: 0,
payload: StdHostPayload::DebugGetMsgsResponse {
debug_buf: {
let access = shared_buffer().borrow();
let access = shared_buffer().borrow().map_err(Error::Buffer)?;
let slice: &[u8] = access.borrow();
slice.try_into().unwrap()
},
Expand All @@ -81,14 +86,14 @@ pub async fn defmt_to_host_task() {

// Clear the staged portion of the buffer
{
let mut access = acpi_owned.borrow_mut();
let mut access = acpi_owned.borrow_mut().map_err(Error::Buffer)?;
let buf: &mut [u8] = BorrowMut::borrow_mut(&mut access);
buf[..copy_len].fill(0);
}
}
}

pub async fn no_avail_to_host_task() {
pub async fn no_avail_to_host_task() -> Result<embedded_services::Never, Error> {
embedded_services::define_static_buffer!(no_avail_acpi_buf, u8, [0u8; 12]);

embedded_services::info!("no avail to host task start");
Expand All @@ -100,7 +105,7 @@ pub async fn no_avail_to_host_task() {

let acpi_owned = no_avail_acpi_buf::get_mut().expect("defmt staging buffer already initialized elsewhere");
{
let mut access = acpi_owned.borrow_mut();
let mut access = acpi_owned.borrow_mut().map_err(Error::Buffer)?;
let buf: &mut [u8] = BorrowMut::borrow_mut(&mut access);
// Use 0xDEADBEEF to signify no frame available
buf[4..12].copy_from_slice(&0xDEADBEEFu64.to_be_bytes());
Expand Down
2 changes: 1 addition & 1 deletion embedded-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ cortex-m.workspace = true
critical-section = { workspace = true, features = ["std"] }
embassy-futures.workspace = true
embassy-sync = { workspace = true, features = ["std"] }
embassy-time = { workspace = true, features = ["std"] }
embassy-time = { workspace = true, features = ["std", "generic-queue-8"] }
embassy-time-driver = { workspace = true }
rstest.workspace = true
static_cell.workspace = true
Expand Down
1 change: 1 addition & 0 deletions embedded-service/src/broadcaster/immediate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl<T: Clone + 'static> Immediate<T> {
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod test {
use super::*;
use embassy_sync::pubsub::{PubSubChannel, WaitResult};
Expand Down
Loading