Skip to content

Commit 8ca1ac3

Browse files
committed
usb_hub: provide a topic that signals overloads
This can also be achieved by subscribing to the individual port current topics, but a centralized low-traffic topic is preferable in terms of resource usage. Signed-off-by: Leonard Göhrs <l.goehrs@pengutronix.de>
1 parent 14a1603 commit 8ca1ac3

File tree

4 files changed

+106
-6
lines changed

4 files changed

+106
-6
lines changed

openapi.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,22 @@ paths:
272272
schema:
273273
$ref: '#/components/schemas/UsbDevice'
274274

275+
/v1/usb/host/overload:
276+
get:
277+
summary: Get the name of the currently overloaded port (if any)
278+
tags: [USB Host]
279+
responses:
280+
'200':
281+
content:
282+
application/json:
283+
schema:
284+
type: string
285+
enum:
286+
- Total
287+
- Port1
288+
- Port2
289+
- Port3
290+
275291
/v1/tac/temperatures/soc:
276292
get:
277293
summary: Get the current temperature inside the SoC

src/main.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,13 @@ async fn main() -> Result<()> {
8080
let dig_io = DigitalIo::new(&mut bb, led.out_0.clone(), led.out_1.clone());
8181
let regulators = Regulators::new(&mut bb);
8282
let temperatures = Temperatures::new(&mut bb);
83-
let usb_hub = UsbHub::new(&mut bb);
83+
let usb_hub = UsbHub::new(
84+
&mut bb,
85+
adc.usb_host_curr.fast.clone(),
86+
adc.usb_host1_curr.fast.clone(),
87+
adc.usb_host2_curr.fast.clone(),
88+
adc.usb_host3_curr.fast.clone(),
89+
);
8490

8591
// Expose other software on the TAC via the broker framework by connecting
8692
// to them via HTTP / DBus APIs.

src/ui/screens/usb.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ use super::{
2828
};
2929
use crate::broker::Topic;
3030
use crate::measurement::Measurement;
31+
use crate::usb_hub::{MAX_PORT_CURRENT, MAX_TOTAL_CURRENT};
3132

3233
const SCREEN_TYPE: NormalScreen = NormalScreen::Usb;
33-
const CURRENT_LIMIT_PER_PORT: f32 = 0.5;
34-
const CURRENT_LIMIT_TOTAL: f32 = 0.7;
3534
const OFFSET_INDICATOR: Point = Point::new(92, -10);
3635
const OFFSET_BAR: Point = Point::new(122, -14);
3736
const WIDTH_BAR: u32 = 90;
@@ -103,7 +102,7 @@ impl ActivatableScreen for UsbScreen {
103102
row_anchor(0) + OFFSET_BAR,
104103
WIDTH_BAR,
105104
HEIGHT_BAR,
106-
Box::new(|meas: &Measurement| meas.value / CURRENT_LIMIT_TOTAL),
105+
Box::new(|meas: &Measurement| meas.value / MAX_TOTAL_CURRENT),
107106
)
108107
});
109108

@@ -143,7 +142,7 @@ impl ActivatableScreen for UsbScreen {
143142
anchor_bar,
144143
WIDTH_BAR,
145144
HEIGHT_BAR,
146-
Box::new(|meas: &Measurement| meas.value / CURRENT_LIMIT_PER_PORT),
145+
Box::new(|meas: &Measurement| meas.value / MAX_PORT_CURRENT),
147146
)
148147
});
149148
}

src/usb_hub.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use async_std::sync::Arc;
2323
use async_std::task::{sleep, spawn};
2424
use serde::{Deserialize, Serialize};
2525

26+
use crate::adc::CalibratedChannel;
2627
use crate::broker::{BrokerBuilder, Topic};
2728

2829
#[cfg(feature = "demo_mode")]
@@ -135,6 +136,45 @@ const PORTS: &[(&str, &str)] = &[
135136
),
136137
];
137138

139+
// The total current for all ports is limited to 700mA, the per-port current is
140+
// limited to 500mA.
141+
pub const MAX_TOTAL_CURRENT: f32 = 0.7;
142+
pub const MAX_PORT_CURRENT: f32 = 0.5;
143+
144+
// The measurement is not _that_ exact so start warning at 90% utilization.
145+
const CURRENT_MARGIN: f32 = 0.9;
146+
const WARN_TOTAL_CURRENT: f32 = MAX_TOTAL_CURRENT * CURRENT_MARGIN;
147+
const WARN_PORT_CURRENT: f32 = MAX_PORT_CURRENT * CURRENT_MARGIN;
148+
149+
#[derive(Serialize, Deserialize, Clone, PartialEq)]
150+
pub enum OverloadedPort {
151+
Total,
152+
Port1,
153+
Port2,
154+
Port3,
155+
}
156+
157+
impl OverloadedPort {
158+
fn from_currents(total: f32, port1: f32, port2: f32, port3: f32) -> Option<Self> {
159+
// Based on the maximum / per-port limits it should not be possible for two
160+
// individual ports to be overloaded at the same time while the total is not
161+
// overloaded, so reporting either "total" or one of the ports should be
162+
// sufficient.
163+
164+
if total > WARN_TOTAL_CURRENT {
165+
Some(Self::Total)
166+
} else if port1 > WARN_PORT_CURRENT {
167+
Some(Self::Port1)
168+
} else if port2 > WARN_PORT_CURRENT {
169+
Some(Self::Port2)
170+
} else if port3 > WARN_PORT_CURRENT {
171+
Some(Self::Port3)
172+
} else {
173+
None
174+
}
175+
}
176+
}
177+
138178
#[derive(Serialize, Deserialize, PartialEq, Clone)]
139179
pub struct UsbDevice {
140180
id_product: String,
@@ -151,6 +191,7 @@ pub struct UsbPort {
151191
}
152192

153193
pub struct UsbHub {
194+
pub overload: Arc<Topic<Option<OverloadedPort>>>,
154195
pub port1: UsbPort,
155196
pub port2: UsbPort,
156197
pub port3: UsbPort,
@@ -236,11 +277,49 @@ fn handle_port(bb: &mut BrokerBuilder, name: &'static str, base: &'static str) -
236277
port
237278
}
238279

280+
fn handle_overloads(
281+
bb: &mut BrokerBuilder,
282+
total: CalibratedChannel,
283+
port1: CalibratedChannel,
284+
port2: CalibratedChannel,
285+
port3: CalibratedChannel,
286+
) -> Arc<Topic<Option<OverloadedPort>>> {
287+
let overload = bb.topic_ro("/v1/usb/host/overload", None);
288+
289+
let overload_task = overload.clone();
290+
291+
spawn(async move {
292+
loop {
293+
let overloaded_port = OverloadedPort::from_currents(
294+
total.get().map(|m| m.value).unwrap_or(0.0),
295+
port1.get().map(|m| m.value).unwrap_or(0.0),
296+
port2.get().map(|m| m.value).unwrap_or(0.0),
297+
port3.get().map(|m| m.value).unwrap_or(0.0),
298+
);
299+
300+
overload_task.set_if_changed(overloaded_port);
301+
302+
sleep(POLL_INTERVAL).await;
303+
}
304+
});
305+
306+
overload
307+
}
308+
239309
impl UsbHub {
240-
pub fn new(bb: &mut BrokerBuilder) -> Self {
310+
pub fn new(
311+
bb: &mut BrokerBuilder,
312+
total: CalibratedChannel,
313+
port1: CalibratedChannel,
314+
port2: CalibratedChannel,
315+
port3: CalibratedChannel,
316+
) -> Self {
317+
let overload = handle_overloads(bb, total, port1, port2, port3);
318+
241319
let mut ports = PORTS.iter().map(|(name, base)| handle_port(bb, name, base));
242320

243321
Self {
322+
overload,
244323
port1: ports.next().unwrap(),
245324
port2: ports.next().unwrap(),
246325
port3: ports.next().unwrap(),

0 commit comments

Comments
 (0)