aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-01-18 11:30:44 +0000
committerGitHub <[email protected]>2023-01-18 11:30:44 +0000
commitf0ae1f9133463459a7ad06aa15fb899c2f4185e2 (patch)
tree61f2ad783a8338754ca2fb6bb68af7f0cb6bd089
parentdb8e9efe731c8f37927f9d1debac4628c9894204 (diff)
parentb1203bf036454bf28903fc079086e3cc93a18446 (diff)
Merge #1159
1159: stm32 usb otg bug fixes r=Dirbaio a=chemicstry This fixes a couple of usb otg bugs that surfaced with `usb_ethernet` example from nrf: - Properly implemented `Endpoint::wait_enabled()` - Return `EndpointError::Disabled` when neccessary in `Endpoint::write()` Co-authored-by: chemicstry <[email protected]>
-rw-r--r--embassy-stm32/src/usb_otg/usb.rs59
-rw-r--r--examples/stm32f4/Cargo.toml6
-rw-r--r--examples/stm32f4/src/bin/usb_ethernet.rs169
3 files changed, 223 insertions, 11 deletions
diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs
index 2d9b613ea..fd2f67073 100644
--- a/embassy-stm32/src/usb_otg/usb.rs
+++ b/embassy-stm32/src/usb_otg/usb.rs
@@ -791,6 +791,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
791 w.set_usbaep(enabled); 791 w.set_usbaep(enabled);
792 }) 792 })
793 }); 793 });
794
795 // Wake `Endpoint::wait_enabled()`
796 T::state().ep_out_wakers[ep_addr.index()].wake();
794 } 797 }
795 Direction::In => { 798 Direction::In => {
796 // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW 799 // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW
@@ -807,6 +810,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
807 w.set_usbaep(enabled); 810 w.set_usbaep(enabled);
808 }) 811 })
809 }); 812 });
813
814 // Wake `Endpoint::wait_enabled()`
815 T::state().ep_in_wakers[ep_addr.index()].wake();
810 } 816 }
811 } 817 }
812 } 818 }
@@ -1031,7 +1037,21 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> {
1031 &self.info 1037 &self.info
1032 } 1038 }
1033 1039
1034 async fn wait_enabled(&mut self) {} 1040 async fn wait_enabled(&mut self) {
1041 poll_fn(|cx| {
1042 let ep_index = self.info.addr.index();
1043
1044 T::state().ep_in_wakers[ep_index].register(cx.waker());
1045
1046 // SAFETY: atomic read without side effects
1047 if unsafe { T::regs().diepctl(ep_index).read().usbaep() } {
1048 Poll::Ready(())
1049 } else {
1050 Poll::Pending
1051 }
1052 })
1053 .await
1054 }
1035} 1055}
1036 1056
1037impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { 1057impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> {
@@ -1039,7 +1059,21 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> {
1039 &self.info 1059 &self.info
1040 } 1060 }
1041 1061
1042 async fn wait_enabled(&mut self) {} 1062 async fn wait_enabled(&mut self) {
1063 poll_fn(|cx| {
1064 let ep_index = self.info.addr.index();
1065
1066 T::state().ep_out_wakers[ep_index].register(cx.waker());
1067
1068 // SAFETY: atomic read without side effects
1069 if unsafe { T::regs().doepctl(ep_index).read().usbaep() } {
1070 Poll::Ready(())
1071 } else {
1072 Poll::Pending
1073 }
1074 })
1075 .await
1076 }
1043} 1077}
1044 1078
1045impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { 1079impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> {
@@ -1092,6 +1126,8 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> {
1092 1126
1093impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { 1127impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
1094 async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { 1128 async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {
1129 trace!("write ep={:?} data={:?}", self.info.addr, buf);
1130
1095 if buf.len() > self.info.max_packet_size as usize { 1131 if buf.len() > self.info.max_packet_size as usize {
1096 return Err(EndpointError::BufferOverflow); 1132 return Err(EndpointError::BufferOverflow);
1097 } 1133 }
@@ -1100,18 +1136,21 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
1100 let index = self.info.addr.index(); 1136 let index = self.info.addr.index();
1101 let state = T::state(); 1137 let state = T::state();
1102 1138
1103 // Wait for previous transfer to complete 1139 // Wait for previous transfer to complete and check if endpoint is disabled
1104 poll_fn(|cx| { 1140 poll_fn(|cx| {
1105 state.ep_in_wakers[index].register(cx.waker()); 1141 state.ep_in_wakers[index].register(cx.waker());
1106 1142
1107 // SAFETY: atomic read with no side effects 1143 // SAFETY: atomic read with no side effects
1108 if unsafe { r.diepctl(index).read().epena() } { 1144 let diepctl = unsafe { r.diepctl(index).read() };
1109 Poll::Pending 1145 if !diepctl.usbaep() {
1146 Poll::Ready(Err(EndpointError::Disabled))
1147 } else if !diepctl.epena() {
1148 Poll::Ready(Ok(()))
1110 } else { 1149 } else {
1111 Poll::Ready(()) 1150 Poll::Pending
1112 } 1151 }
1113 }) 1152 })
1114 .await; 1153 .await?;
1115 1154
1116 if buf.len() > 0 { 1155 if buf.len() > 0 {
1117 poll_fn(|cx| { 1156 poll_fn(|cx| {
@@ -1167,7 +1206,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
1167 unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; 1206 unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) };
1168 } 1207 }
1169 1208
1170 trace!("WRITE OK"); 1209 trace!("write done ep={:?}", self.info.addr);
1171 1210
1172 Ok(()) 1211 Ok(())
1173 } 1212 }
@@ -1205,8 +1244,8 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> {
1205 // Clear NAK to indicate we are ready to receive more data 1244 // Clear NAK to indicate we are ready to receive more data
1206 T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { 1245 T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| {
1207 w.set_cnak(true); 1246 w.set_cnak(true);
1208 }) 1247 });
1209 }; 1248 }
1210 1249
1211 trace!("SETUP received: {:?}", data); 1250 trace!("SETUP received: {:?}", data);
1212 Poll::Ready(data) 1251 Poll::Ready(data)
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 252d60855..e2b17bfcb 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -4,13 +4,13 @@ name = "embassy-stm32f4-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7
8[dependencies] 7[dependencies]
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
13embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"], optional = true }
14 14
15defmt = "0.3" 15defmt = "0.3"
16defmt-rtt = "0.4" 16defmt-rtt = "0.4"
@@ -27,5 +27,9 @@ embedded-storage = "0.3.0"
27micromath = "2.0.0" 27micromath = "2.0.0"
28static_cell = "1.0" 28static_cell = "1.0"
29 29
30[[bin]]
31name = "usb_ethernet"
32required-features = ["embassy-net"]
33
30[profile.release] 34[profile.release]
31debug = 2 35debug = 2
diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs
new file mode 100644
index 000000000..cf2885ae5
--- /dev/null
+++ b/examples/stm32f4/src/bin/usb_ethernet.rs
@@ -0,0 +1,169 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_net::tcp::TcpSocket;
8use embassy_net::{Stack, StackResources};
9use embassy_stm32::rng::Rng;
10use embassy_stm32::time::mhz;
11use embassy_stm32::usb_otg::Driver;
12use embassy_stm32::{interrupt, Config};
13use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};
14use embassy_usb::class::cdc_ncm::{CdcNcmClass, State};
15use embassy_usb::{Builder, UsbDevice};
16use embedded_io::asynch::Write;
17use static_cell::StaticCell;
18use {defmt_rtt as _, panic_probe as _};
19
20type UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>;
21
22macro_rules! singleton {
23 ($val:expr) => {{
24 type T = impl Sized;
25 static STATIC_CELL: StaticCell<T> = StaticCell::new();
26 let (x,) = STATIC_CELL.init(($val,));
27 x
28 }};
29}
30
31const MTU: usize = 1514;
32
33#[embassy_executor::task]
34async fn usb_task(mut device: UsbDevice<'static, UsbDriver>) -> ! {
35 device.run().await
36}
37
38#[embassy_executor::task]
39async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! {
40 class.run().await
41}
42
43#[embassy_executor::task]
44async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! {
45 stack.run().await
46}
47
48#[embassy_executor::main]
49async fn main(spawner: Spawner) {
50 info!("Hello World!");
51
52 let mut config = Config::default();
53 config.rcc.pll48 = true;
54 config.rcc.sys_ck = Some(mhz(48));
55
56 let p = embassy_stm32::init(config);
57
58 // Create the driver, from the HAL.
59 let irq = interrupt::take!(OTG_FS);
60 let ep_out_buffer = &mut singleton!([0; 256])[..];
61 let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, ep_out_buffer);
62
63 // Create embassy-usb Config
64 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
65 config.manufacturer = Some("Embassy");
66 config.product = Some("USB-Ethernet example");
67 config.serial_number = Some("12345678");
68 config.max_power = 100;
69 config.max_packet_size_0 = 64;
70
71 // Required for Windows support.
72 config.composite_with_iads = true;
73 config.device_class = 0xEF;
74 config.device_sub_class = 0x02;
75 config.device_protocol = 0x01;
76
77 // Create embassy-usb DeviceBuilder using the driver and config.
78 let mut builder = Builder::new(
79 driver,
80 config,
81 &mut singleton!([0; 256])[..],
82 &mut singleton!([0; 256])[..],
83 &mut singleton!([0; 256])[..],
84 &mut singleton!([0; 128])[..],
85 None,
86 );
87
88 // Our MAC addr.
89 let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];
90 // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has.
91 let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];
92
93 // Create classes on the builder.
94 let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64);
95
96 // Build the builder.
97 let usb = builder.build();
98
99 unwrap!(spawner.spawn(usb_task(usb)));
100
101 let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr);
102 unwrap!(spawner.spawn(usb_ncm_task(runner)));
103
104 let config = embassy_net::ConfigStrategy::Dhcp;
105 //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
106 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
107 // dns_servers: Vec::new(),
108 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
109 //});
110
111 // Generate random seed
112 let mut rng = Rng::new(p.RNG);
113 let mut seed = [0; 8];
114 unwrap!(rng.async_fill_bytes(&mut seed).await);
115 let seed = u64::from_le_bytes(seed);
116
117 // Init network stack
118 let stack = &*singleton!(Stack::new(
119 device,
120 config,
121 singleton!(StackResources::<1, 2, 8>::new()),
122 seed
123 ));
124
125 unwrap!(spawner.spawn(net_task(stack)));
126
127 // And now we can use it!
128
129 let mut rx_buffer = [0; 4096];
130 let mut tx_buffer = [0; 4096];
131 let mut buf = [0; 4096];
132
133 loop {
134 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
135 socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
136
137 info!("Listening on TCP:1234...");
138 if let Err(e) = socket.accept(1234).await {
139 warn!("accept error: {:?}", e);
140 continue;
141 }
142
143 info!("Received connection from {:?}", socket.remote_endpoint());
144
145 loop {
146 let n = match socket.read(&mut buf).await {
147 Ok(0) => {
148 warn!("read EOF");
149 break;
150 }
151 Ok(n) => n,
152 Err(e) => {
153 warn!("read error: {:?}", e);
154 break;
155 }
156 };
157
158 info!("rxd {:02x}", &buf[..n]);
159
160 match socket.write_all(&buf[..n]).await {
161 Ok(()) => {}
162 Err(e) => {
163 warn!("write error: {:?}", e);
164 break;
165 }
166 };
167 }
168 }
169}