Skip to content

Commit a542fb7

Browse files
authored
Merge pull request #96 from RobertZ2011/type-c
Add initial type-C data structures and messages
2 parents 26d3d23 + f8b0b9e commit a542fb7

File tree

8 files changed

+567
-0
lines changed

8 files changed

+567
-0
lines changed

embedded-service/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ embedded-hal-nb = { version = "1.0" }
4141

4242
document-features = "0.2.7"
4343

44+
bitfield = "0.17.0"
45+
4446
[dev-dependencies]
4547
embassy-sync = { git = "https://github.com/embassy-rs/embassy", features = [
4648
"std",

embedded-service/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod buffer;
1212
pub mod comms;
1313
pub mod fmt;
1414
pub mod hid;
15+
pub mod type_c;
1516

1617
/// initialize all service static interfaces as required. Ideally, this is done before subsystem initialization
1718
pub async fn init() {
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
//! PD controller related code
2+
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
3+
use embassy_sync::channel::Channel;
4+
use embassy_sync::once_lock::OnceLock;
5+
use embassy_time::{with_timeout, Duration};
6+
7+
use super::ucsi::lpm;
8+
use super::{ControllerId, Error, PortId};
9+
use crate::intrusive_list;
10+
11+
/// PD controller command-specific data
12+
#[derive(Copy, Clone, Debug)]
13+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14+
pub enum InternalCommandData {
15+
/// Reset the PD controller
16+
Reset,
17+
}
18+
19+
/// PD controller command
20+
#[derive(Copy, Clone, Debug)]
21+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22+
pub enum Command {
23+
/// Controller specific command
24+
Controller(InternalCommandData),
25+
/// UCSI command passthrough
26+
Lpm(lpm::Command),
27+
}
28+
29+
/// Controller-specific response data
30+
#[derive(Copy, Clone, Debug)]
31+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32+
pub enum InternalResponseData {
33+
/// Command complete
34+
Complete,
35+
}
36+
37+
/// Response for controller-specific commands
38+
pub type InternalResponse = Result<InternalResponseData, Error>;
39+
40+
/// PD controller command response
41+
#[derive(Copy, Clone, Debug)]
42+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43+
pub enum Response {
44+
/// Controller response
45+
Controller(InternalResponse),
46+
/// UCSI response passthrough
47+
Lpm(lpm::Response),
48+
}
49+
50+
/// PD controller
51+
pub struct Controller<'a> {
52+
node: intrusive_list::Node,
53+
id: ControllerId,
54+
ports: &'a [PortId],
55+
command: Channel<NoopRawMutex, Command, 1>,
56+
response: Channel<NoopRawMutex, Response, 1>,
57+
}
58+
59+
impl intrusive_list::NodeContainer for Controller<'static> {
60+
fn get_node(&self) -> &intrusive_list::Node {
61+
&self.node
62+
}
63+
}
64+
65+
impl<'a> Controller<'a> {
66+
/// Create a new PD controller struct
67+
pub fn new(id: ControllerId, ports: &'a [PortId]) -> Self {
68+
Self {
69+
node: intrusive_list::Node::uninit(),
70+
id,
71+
ports,
72+
command: Channel::new(),
73+
response: Channel::new(),
74+
}
75+
}
76+
77+
/// Send a command to this controller
78+
pub async fn send_command(&self, command: Command) -> Response {
79+
self.command.send(command).await;
80+
self.response.receive().await
81+
}
82+
83+
/// Check if this controller has the given port
84+
pub fn has_port(&self, port: PortId) -> bool {
85+
self.ports.iter().any(|p| *p == port)
86+
}
87+
88+
/// Wait for a command to be sent to this controller
89+
pub async fn wait_command(&self) -> Command {
90+
self.command.receive().await
91+
}
92+
93+
/// Send response
94+
pub async fn send_response(&self, response: Response) {
95+
self.response.send(response).await;
96+
}
97+
}
98+
99+
/// Trait for types that contain a controller struct
100+
pub trait ControllerContainer {
101+
/// Get the controller struct
102+
fn get_controller<'a>(&'a self) -> &'a Controller<'a>;
103+
}
104+
105+
/// Internal context for managing PD controllers
106+
struct Context {
107+
controllers: intrusive_list::IntrusiveList,
108+
}
109+
110+
impl Context {
111+
fn new() -> Self {
112+
Self {
113+
controllers: intrusive_list::IntrusiveList::new(),
114+
}
115+
}
116+
}
117+
118+
static CONTEXT: OnceLock<Context> = OnceLock::new();
119+
120+
/// Initialize the PD controller context
121+
pub fn init() {
122+
CONTEXT.get_or_init(Context::new);
123+
}
124+
125+
/// Register a PD controller
126+
pub async fn register_controller(controller: &'static impl ControllerContainer) -> Result<(), intrusive_list::Error> {
127+
CONTEXT.get().await.controllers.push(controller.get_controller())
128+
}
129+
130+
/// Default timeout for PD controller commands
131+
const DEFAULT_TIMEOUT: Duration = Duration::from_millis(250);
132+
133+
/// Send a command to the given controller with no timeout
134+
async fn send_controller_command_no_timeout(
135+
controller_id: ControllerId,
136+
command: InternalCommandData,
137+
) -> Result<InternalResponseData, Error> {
138+
let node = CONTEXT
139+
.get()
140+
.await
141+
.controllers
142+
.into_iter()
143+
.find(|node| {
144+
if let Some(controller) = node.data::<Controller>() {
145+
controller.id == controller_id
146+
} else {
147+
false
148+
}
149+
})
150+
.map_or(Error::InvalidController.into(), Ok)?;
151+
152+
match node
153+
.data::<Controller>()
154+
.ok_or(Error::InvalidController)?
155+
.send_command(Command::Controller(command))
156+
.await
157+
{
158+
Response::Controller(response) => response,
159+
_ => Error::InvalidResponse.into(),
160+
}
161+
}
162+
163+
/// Send a command to the given controller with a timeout
164+
async fn send_controller_command(
165+
controller_id: ControllerId,
166+
command: InternalCommandData,
167+
timeout: Duration,
168+
) -> Result<InternalResponseData, Error> {
169+
match with_timeout(timeout, send_controller_command_no_timeout(controller_id, command)).await {
170+
Ok(response) => response,
171+
Err(_) => Error::Timeout.into(),
172+
}
173+
}
174+
175+
/// Reset the given controller
176+
pub async fn reset_controller(controller_id: ControllerId) -> Result<(), Error> {
177+
send_controller_command(controller_id, InternalCommandData::Reset, DEFAULT_TIMEOUT)
178+
.await
179+
.map(|_| ())
180+
}
181+
182+
/// Send a command to the given port
183+
async fn send_port_command_no_timeout(port_id: PortId, command: lpm::CommandData) -> Result<lpm::ResponseData, Error> {
184+
let node = CONTEXT
185+
.get()
186+
.await
187+
.controllers
188+
.into_iter()
189+
.find(|node| {
190+
if let Some(controller) = node.data::<Controller>() {
191+
controller.has_port(port_id)
192+
} else {
193+
false
194+
}
195+
})
196+
.map_or(Error::InvalidPort.into(), Ok)?;
197+
198+
match node
199+
.data::<Controller>()
200+
.ok_or(Error::InvalidController)?
201+
.send_command(Command::Lpm(lpm::Command {
202+
port: port_id,
203+
operation: command,
204+
}))
205+
.await
206+
{
207+
Response::Lpm(response) => response,
208+
_ => Error::InvalidResponse.into(),
209+
}
210+
}
211+
212+
/// Send a command to the given port with a timeout
213+
async fn send_port_command(
214+
port_id: PortId,
215+
command: lpm::CommandData,
216+
timeout: Duration,
217+
) -> Result<lpm::ResponseData, Error> {
218+
match with_timeout(timeout, send_port_command_no_timeout(port_id, command)).await {
219+
Ok(response) => response,
220+
Err(_) => Error::Timeout.into(),
221+
}
222+
}
223+
224+
/// Resets the given port
225+
pub async fn reset_port(port_id: PortId, reset_type: lpm::ResetType) -> Result<lpm::ResponseData, Error> {
226+
send_port_command(port_id, lpm::CommandData::ConnectorReset(reset_type), DEFAULT_TIMEOUT).await
227+
}

embedded-service/src/type_c/mod.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//! Type-C service
2+
3+
pub mod controller;
4+
pub mod ucsi;
5+
6+
/// Port ID
7+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9+
pub struct PortId(pub u8);
10+
11+
/// Controller ID
12+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14+
pub struct ControllerId(pub u8);
15+
16+
/// PD controller error
17+
#[derive(Copy, Clone, Debug)]
18+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19+
pub enum Error {
20+
/// Transport error
21+
Transport,
22+
/// Bus error
23+
Bus,
24+
/// Invalid controller
25+
InvalidController,
26+
/// Invalid response
27+
InvalidResponse,
28+
/// Unrecognized command
29+
UnrecognizedCommand,
30+
/// Invalid port
31+
InvalidPort,
32+
/// Invalid parameters
33+
InvalidParams,
34+
/// Incompatible partner
35+
IncompatiblePartner,
36+
/// CC communication error,
37+
CcCommunication,
38+
/// Failed due to dead battery condition
39+
DeadBattery,
40+
/// Contract negociation failed
41+
ContractNegociation,
42+
/// Overcurrent
43+
Overcurrent,
44+
/// Swap rejected by port partner
45+
SwapRejectedPartner,
46+
/// Hard reset
47+
HardReset,
48+
/// Policy conflict
49+
PolicyConflict,
50+
/// Swap rejected
51+
SwapRejected,
52+
/// Reverse current protection
53+
ReverseCurrent,
54+
/// Set sink path rejected
55+
SetSinkPath,
56+
/// Timeout
57+
Timeout,
58+
}
59+
60+
impl<T> Into<Result<T, Error>> for Error {
61+
fn into(self) -> Result<T, Error> {
62+
Err(self)
63+
}
64+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use crate::type_c::{Error, PortId};
2+
3+
/// Connector reset types
4+
#[derive(Copy, Clone, Debug)]
5+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6+
pub enum ResetType {
7+
Hard,
8+
Data,
9+
}
10+
11+
/// LPM command data
12+
#[derive(Copy, Clone, Debug)]
13+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14+
pub enum CommandData {
15+
ConnectorReset(ResetType),
16+
}
17+
18+
/// LPM commands
19+
#[derive(Copy, Clone, Debug)]
20+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21+
pub struct Command {
22+
pub port: PortId,
23+
pub operation: CommandData,
24+
}
25+
26+
/// LPM response data
27+
#[derive(Copy, Clone, Debug)]
28+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29+
pub enum ResponseData {
30+
Complete,
31+
}
32+
33+
pub type Response = Result<ResponseData, Error>;

0 commit comments

Comments
 (0)