aboutsummaryrefslogtreecommitdiff
path: root/examples/nrf/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-04-07 23:03:39 +0000
committerGitHub <[email protected]>2022-04-07 23:03:39 +0000
commit37da84129da12280cfb0a4f016639d517a0f637e (patch)
treea638b15c2129c5fd4d7475be57574c27f445c652 /examples/nrf/src
parent637ec36f9c619c8aa1f844d1499492420aa36b29 (diff)
parent9252e8bb88236946597575918991b55bc10596b3 (diff)
Merge #657
657: Async usb stack r=Dirbaio a=Dirbaio TODO - [x] Make it work on nRF - [x] Add a way for classes to handle their own EP0 control requests - thanks `@alexmoon!` - [x] Handle CONTROL OUT requests with data. - [ ] Impl AsyncRead/AsyncWrite for CDC ACM -- will do later, it's not trivial - [x] Cleanup unwraps/asserts/panics - [x] Cleanup logs (make everything trace/debug, not info) - [ ] Port synopsys-usb-otg - [ ] Port stm32-usbd - [ ] Add more classes? HID, MSD? Co-authored-by: Dario Nieuwenhuis <[email protected]> Co-authored-by: alexmoon <[email protected]>
Diffstat (limited to 'examples/nrf/src')
-rw-r--r--examples/nrf/src/bin/usb_hid_keyboard.rs148
-rw-r--r--examples/nrf/src/bin/usb_hid_mouse.rs129
-rw-r--r--examples/nrf/src/bin/usb_serial.rs113
-rw-r--r--examples/nrf/src/bin/usb_serial_multitask.rs122
-rw-r--r--examples/nrf/src/bin/usb_uart.rs89
-rw-r--r--examples/nrf/src/bin/usb_uart_io.rs66
6 files changed, 512 insertions, 155 deletions
diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs
new file mode 100644
index 000000000..0812697e4
--- /dev/null
+++ b/examples/nrf/src/bin/usb_hid_keyboard.rs
@@ -0,0 +1,148 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use core::mem;
7use defmt::*;
8use embassy::executor::Spawner;
9use embassy::time::Duration;
10use embassy_nrf::gpio::{Input, Pin, Pull};
11use embassy_nrf::interrupt;
12use embassy_nrf::pac;
13use embassy_nrf::usb::Driver;
14use embassy_nrf::Peripherals;
15use embassy_usb::control::OutResponse;
16use embassy_usb::{Config, UsbDeviceBuilder};
17use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State};
18use futures::future::join;
19use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
20
21use defmt_rtt as _; // global logger
22use panic_probe as _;
23
24#[embassy::main]
25async fn main(_spawner: Spawner, p: Peripherals) {
26 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
27 let power: pac::POWER = unsafe { mem::transmute(()) };
28
29 info!("Enabling ext hfosc...");
30 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
31 while clock.events_hfclkstarted.read().bits() != 1 {}
32
33 info!("Waiting for vbus...");
34 while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
35 info!("vbus OK");
36
37 // Create the driver, from the HAL.
38 let irq = interrupt::take!(USBD);
39 let driver = Driver::new(p.USBD, irq);
40
41 // Create embassy-usb Config
42 let mut config = Config::new(0xc0de, 0xcafe);
43 config.manufacturer = Some("Tactile Engineering");
44 config.product = Some("Testy");
45 config.serial_number = Some("12345678");
46 config.max_power = 100;
47 config.max_packet_size_0 = 64;
48
49 // Create embassy-usb DeviceBuilder using the driver and config.
50 // It needs some buffers for building the descriptors.
51 let mut device_descriptor = [0; 256];
52 let mut config_descriptor = [0; 256];
53 let mut bos_descriptor = [0; 256];
54 let mut control_buf = [0; 16];
55 let request_handler = MyRequestHandler {};
56
57 let mut state = State::<8, 1>::new();
58
59 let mut builder = UsbDeviceBuilder::new(
60 driver,
61 config,
62 &mut device_descriptor,
63 &mut config_descriptor,
64 &mut bos_descriptor,
65 &mut control_buf,
66 );
67
68 // Create classes on the builder.
69 let hid = HidClass::with_output_ep(
70 &mut builder,
71 &mut state,
72 KeyboardReport::desc(),
73 Some(&request_handler),
74 60,
75 64,
76 );
77
78 // Build the builder.
79 let mut usb = builder.build().await;
80
81 // Run the USB device.
82 let usb_fut = usb.run();
83
84 let mut button = Input::new(p.P0_11.degrade(), Pull::Up);
85
86 let (mut hid_in, hid_out) = hid.split();
87
88 // Do stuff with the class!
89 let in_fut = async {
90 loop {
91 button.wait_for_low().await;
92 info!("PRESSED");
93 let report = KeyboardReport {
94 keycodes: [4, 0, 0, 0, 0, 0],
95 leds: 0,
96 modifier: 0,
97 reserved: 0,
98 };
99 match hid_in.serialize(&report).await {
100 Ok(()) => {}
101 Err(e) => warn!("Failed to send report: {:?}", e),
102 };
103
104 button.wait_for_high().await;
105 info!("RELEASED");
106 let report = KeyboardReport {
107 keycodes: [0, 0, 0, 0, 0, 0],
108 leds: 0,
109 modifier: 0,
110 reserved: 0,
111 };
112 match hid_in.serialize(&report).await {
113 Ok(()) => {}
114 Err(e) => warn!("Failed to send report: {:?}", e),
115 };
116 }
117 };
118
119 let out_fut = async {
120 hid_out.run(false, &request_handler).await;
121 };
122 // Run everything concurrently.
123 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
124 join(usb_fut, join(in_fut, out_fut)).await;
125}
126
127struct MyRequestHandler {}
128
129impl RequestHandler for MyRequestHandler {
130 fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
131 info!("Get report for {:?}", id);
132 None
133 }
134
135 fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse {
136 info!("Set report for {:?}: {=[u8]}", id, data);
137 OutResponse::Accepted
138 }
139
140 fn set_idle(&self, id: Option<ReportId>, dur: Duration) {
141 info!("Set idle rate for {:?} to {:?}", id, dur);
142 }
143
144 fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> {
145 info!("Get idle rate for {:?}", id);
146 None
147 }
148}
diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs
new file mode 100644
index 000000000..ca9383827
--- /dev/null
+++ b/examples/nrf/src/bin/usb_hid_mouse.rs
@@ -0,0 +1,129 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use core::mem;
7use defmt::*;
8use embassy::executor::Spawner;
9use embassy::time::{Duration, Timer};
10use embassy_nrf::interrupt;
11use embassy_nrf::pac;
12use embassy_nrf::usb::Driver;
13use embassy_nrf::Peripherals;
14use embassy_usb::control::OutResponse;
15use embassy_usb::{Config, UsbDeviceBuilder};
16use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State};
17use futures::future::join;
18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
19
20use defmt_rtt as _; // global logger
21use panic_probe as _;
22
23#[embassy::main]
24async fn main(_spawner: Spawner, p: Peripherals) {
25 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
26 let power: pac::POWER = unsafe { mem::transmute(()) };
27
28 info!("Enabling ext hfosc...");
29 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
30 while clock.events_hfclkstarted.read().bits() != 1 {}
31
32 info!("Waiting for vbus...");
33 while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
34 info!("vbus OK");
35
36 // Create the driver, from the HAL.
37 let irq = interrupt::take!(USBD);
38 let driver = Driver::new(p.USBD, irq);
39
40 // Create embassy-usb Config
41 let mut config = Config::new(0xc0de, 0xcafe);
42 config.manufacturer = Some("Tactile Engineering");
43 config.product = Some("Testy");
44 config.serial_number = Some("12345678");
45 config.max_power = 100;
46
47 // Create embassy-usb DeviceBuilder using the driver and config.
48 // It needs some buffers for building the descriptors.
49 let mut device_descriptor = [0; 256];
50 let mut config_descriptor = [0; 256];
51 let mut bos_descriptor = [0; 256];
52 let mut control_buf = [0; 16];
53 let request_handler = MyRequestHandler {};
54
55 let mut control = State::<5, 0>::new();
56
57 let mut builder = UsbDeviceBuilder::new(
58 driver,
59 config,
60 &mut device_descriptor,
61 &mut config_descriptor,
62 &mut bos_descriptor,
63 &mut control_buf,
64 );
65
66 // Create classes on the builder.
67 let mut hid = HidClass::new(
68 &mut builder,
69 &mut control,
70 MouseReport::desc(),
71 Some(&request_handler),
72 60,
73 8,
74 );
75
76 // Build the builder.
77 let mut usb = builder.build().await;
78
79 // Run the USB device.
80 let usb_fut = usb.run();
81
82 // Do stuff with the class!
83 let hid_fut = async {
84 let mut y: i8 = 5;
85 loop {
86 Timer::after(Duration::from_millis(500)).await;
87
88 y = -y;
89 let report = MouseReport {
90 buttons: 0,
91 x: 0,
92 y,
93 wheel: 0,
94 pan: 0,
95 };
96 match hid.input().serialize(&report).await {
97 Ok(()) => {}
98 Err(e) => warn!("Failed to send report: {:?}", e),
99 }
100 }
101 };
102
103 // Run everything concurrently.
104 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
105 join(usb_fut, hid_fut).await;
106}
107
108struct MyRequestHandler {}
109
110impl RequestHandler for MyRequestHandler {
111 fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
112 info!("Get report for {:?}", id);
113 None
114 }
115
116 fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse {
117 info!("Set report for {:?}: {=[u8]}", id, data);
118 OutResponse::Accepted
119 }
120
121 fn set_idle(&self, id: Option<ReportId>, dur: Duration) {
122 info!("Set idle rate for {:?} to {:?}", id, dur);
123 }
124
125 fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> {
126 info!("Get idle rate for {:?}", id);
127 None
128 }
129}
diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs
new file mode 100644
index 000000000..500be2ce8
--- /dev/null
+++ b/examples/nrf/src/bin/usb_serial.rs
@@ -0,0 +1,113 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use core::mem;
7use defmt::{info, panic};
8use embassy::executor::Spawner;
9use embassy_nrf::interrupt;
10use embassy_nrf::pac;
11use embassy_nrf::usb::{Driver, Instance};
12use embassy_nrf::Peripherals;
13use embassy_usb::driver::{ReadError, WriteError};
14use embassy_usb::{Config, UsbDeviceBuilder};
15use embassy_usb_serial::{CdcAcmClass, State};
16use futures::future::join;
17
18use defmt_rtt as _; // global logger
19use panic_probe as _;
20
21#[embassy::main]
22async fn main(_spawner: Spawner, p: Peripherals) {
23 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
24 let power: pac::POWER = unsafe { mem::transmute(()) };
25
26 info!("Enabling ext hfosc...");
27 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
28 while clock.events_hfclkstarted.read().bits() != 1 {}
29
30 info!("Waiting for vbus...");
31 while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
32 info!("vbus OK");
33
34 // Create the driver, from the HAL.
35 let irq = interrupt::take!(USBD);
36 let driver = Driver::new(p.USBD, irq);
37
38 // Create embassy-usb Config
39 let config = Config::new(0xc0de, 0xcafe);
40
41 // Create embassy-usb DeviceBuilder using the driver and config.
42 // It needs some buffers for building the descriptors.
43 let mut device_descriptor = [0; 256];
44 let mut config_descriptor = [0; 256];
45 let mut bos_descriptor = [0; 256];
46 let mut control_buf = [0; 7];
47
48 let mut state = State::new();
49
50 let mut builder = UsbDeviceBuilder::new(
51 driver,
52 config,
53 &mut device_descriptor,
54 &mut config_descriptor,
55 &mut bos_descriptor,
56 &mut control_buf,
57 );
58
59 // Create classes on the builder.
60 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
61
62 // Build the builder.
63 let mut usb = builder.build().await;
64
65 // Run the USB device.
66 let usb_fut = usb.run();
67
68 // Do stuff with the class!
69 let echo_fut = async {
70 loop {
71 class.wait_connection().await;
72 info!("Connected");
73 let _ = echo(&mut class).await;
74 info!("Disconnected");
75 }
76 };
77
78 // Run everything concurrently.
79 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
80 join(usb_fut, echo_fut).await;
81}
82
83struct Disconnected {}
84
85impl From<ReadError> for Disconnected {
86 fn from(val: ReadError) -> Self {
87 match val {
88 ReadError::BufferOverflow => panic!("Buffer overflow"),
89 ReadError::Disabled => Disconnected {},
90 }
91 }
92}
93
94impl From<WriteError> for Disconnected {
95 fn from(val: WriteError) -> Self {
96 match val {
97 WriteError::BufferOverflow => panic!("Buffer overflow"),
98 WriteError::Disabled => Disconnected {},
99 }
100 }
101}
102
103async fn echo<'d, T: Instance + 'd>(
104 class: &mut CdcAcmClass<'d, Driver<'d, T>>,
105) -> Result<(), Disconnected> {
106 let mut buf = [0; 64];
107 loop {
108 let n = class.read_packet(&mut buf).await?;
109 let data = &buf[..n];
110 info!("data: {:x}", data);
111 class.write_packet(data).await?;
112 }
113}
diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs
new file mode 100644
index 000000000..1258bc53d
--- /dev/null
+++ b/examples/nrf/src/bin/usb_serial_multitask.rs
@@ -0,0 +1,122 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use core::mem;
7use defmt::{info, panic, unwrap};
8use embassy::executor::Spawner;
9use embassy::util::Forever;
10use embassy_nrf::pac;
11use embassy_nrf::usb::Driver;
12use embassy_nrf::Peripherals;
13use embassy_nrf::{interrupt, peripherals};
14use embassy_usb::driver::{ReadError, WriteError};
15use embassy_usb::{Config, UsbDevice, UsbDeviceBuilder};
16use embassy_usb_serial::{CdcAcmClass, State};
17
18use defmt_rtt as _; // global logger
19use panic_probe as _;
20
21type MyDriver = Driver<'static, peripherals::USBD>;
22
23#[embassy::task]
24async fn usb_task(mut device: UsbDevice<'static, MyDriver>) {
25 device.run().await;
26}
27
28#[embassy::task]
29async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) {
30 loop {
31 class.wait_connection().await;
32 info!("Connected");
33 let _ = echo(&mut class).await;
34 info!("Disconnected");
35 }
36}
37
38#[embassy::main]
39async fn main(spawner: Spawner, p: Peripherals) {
40 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
41 let power: pac::POWER = unsafe { mem::transmute(()) };
42
43 info!("Enabling ext hfosc...");
44 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
45 while clock.events_hfclkstarted.read().bits() != 1 {}
46
47 info!("Waiting for vbus...");
48 while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
49 info!("vbus OK");
50
51 // Create the driver, from the HAL.
52 let irq = interrupt::take!(USBD);
53 let driver = Driver::new(p.USBD, irq);
54
55 // Create embassy-usb Config
56 let config = Config::new(0xc0de, 0xcafe);
57
58 struct Resources {
59 device_descriptor: [u8; 256],
60 config_descriptor: [u8; 256],
61 bos_descriptor: [u8; 256],
62 control_buf: [u8; 7],
63 serial_state: State<'static>,
64 }
65 static RESOURCES: Forever<Resources> = Forever::new();
66 let res = RESOURCES.put(Resources {
67 device_descriptor: [0; 256],
68 config_descriptor: [0; 256],
69 bos_descriptor: [0; 256],
70 control_buf: [0; 7],
71 serial_state: State::new(),
72 });
73
74 // Create embassy-usb DeviceBuilder using the driver and config.
75 let mut builder = UsbDeviceBuilder::new(
76 driver,
77 config,
78 &mut res.device_descriptor,
79 &mut res.config_descriptor,
80 &mut res.bos_descriptor,
81 &mut res.control_buf,
82 );
83
84 // Create classes on the builder.
85 let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64);
86
87 // Build the builder.
88 let usb = builder.build().await;
89
90 unwrap!(spawner.spawn(usb_task(usb)));
91 unwrap!(spawner.spawn(echo_task(class)));
92}
93
94struct Disconnected {}
95
96impl From<ReadError> for Disconnected {
97 fn from(val: ReadError) -> Self {
98 match val {
99 ReadError::BufferOverflow => panic!("Buffer overflow"),
100 ReadError::Disabled => Disconnected {},
101 }
102 }
103}
104
105impl From<WriteError> for Disconnected {
106 fn from(val: WriteError) -> Self {
107 match val {
108 WriteError::BufferOverflow => panic!("Buffer overflow"),
109 WriteError::Disabled => Disconnected {},
110 }
111 }
112}
113
114async fn echo(class: &mut CdcAcmClass<'static, MyDriver>) -> Result<(), Disconnected> {
115 let mut buf = [0; 64];
116 loop {
117 let n = class.read_packet(&mut buf).await?;
118 let data = &buf[..n];
119 info!("data: {:x}", data);
120 class.write_packet(data).await?;
121 }
122}
diff --git a/examples/nrf/src/bin/usb_uart.rs b/examples/nrf/src/bin/usb_uart.rs
deleted file mode 100644
index d283dccd1..000000000
--- a/examples/nrf/src/bin/usb_uart.rs
+++ /dev/null
@@ -1,89 +0,0 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy::interrupt::InterruptExt;
8use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
9use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial};
10use embassy_nrf::{interrupt, Peripherals};
11use futures::pin_mut;
12use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
13
14use defmt_rtt as _; // global logger
15use panic_probe as _; // print out panic messages
16
17#[embassy::main]
18async fn main(_spawner: Spawner, p: Peripherals) {
19 let mut rx_buffer = [0u8; 64];
20 // we send back input + cr + lf
21 let mut tx_buffer = [0u8; 66];
22
23 let usb_bus = UsbBus::new(p.USBD);
24
25 let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
26
27 let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
28 .manufacturer("Fake company")
29 .product("Serial port")
30 .serial_number("TEST")
31 .device_class(0x02)
32 .build();
33
34 let irq = interrupt::take!(USBD);
35 irq.set_priority(interrupt::Priority::P3);
36
37 let mut state = State::new();
38 let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
39 pin_mut!(usb);
40
41 let (mut reader, mut writer) = usb.as_ref().take_serial_0();
42
43 info!("usb initialized!");
44
45 unwrap!(
46 writer
47 .write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
48 .await
49 );
50
51 let mut buf = [0u8; 64];
52 loop {
53 let mut n = 0;
54
55 async {
56 loop {
57 let char = unwrap!(reader.read_byte().await);
58
59 // throw away, read more on cr, exit on lf
60 if char == b'\r' {
61 continue;
62 } else if char == b'\n' {
63 break;
64 }
65
66 buf[n] = char;
67 n += 1;
68
69 // stop if we're out of room
70 if n == buf.len() {
71 break;
72 }
73 }
74 }
75 .await;
76
77 if n > 0 {
78 for char in buf[..n].iter_mut() {
79 // upper case
80 if 0x61 <= *char && *char <= 0x7a {
81 *char &= !0x20;
82 }
83 }
84 unwrap!(writer.write_all(&buf[..n]).await);
85 unwrap!(writer.write_all(b"\r\n").await);
86 unwrap!(writer.flush().await);
87 }
88 }
89}
diff --git a/examples/nrf/src/bin/usb_uart_io.rs b/examples/nrf/src/bin/usb_uart_io.rs
deleted file mode 100644
index ef2629844..000000000
--- a/examples/nrf/src/bin/usb_uart_io.rs
+++ /dev/null
@@ -1,66 +0,0 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy::interrupt::InterruptExt;
8use embassy::io::{read_line, AsyncWriteExt};
9use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial};
10use embassy_nrf::{interrupt, Peripherals};
11use futures::pin_mut;
12use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
13
14use defmt_rtt as _; // global logger
15use panic_probe as _; // print out panic messages
16
17#[embassy::main]
18async fn main(_spawner: Spawner, p: Peripherals) {
19 let mut rx_buffer = [0u8; 64];
20 // we send back input + cr + lf
21 let mut tx_buffer = [0u8; 66];
22
23 let usb_bus = UsbBus::new(p.USBD);
24
25 let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
26
27 let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
28 .manufacturer("Fake company")
29 .product("Serial port")
30 .serial_number("TEST")
31 .device_class(0x02)
32 .build();
33
34 let irq = interrupt::take!(USBD);
35 irq.set_priority(interrupt::Priority::P3);
36
37 let mut state = State::new();
38 let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
39 pin_mut!(usb);
40
41 let (mut reader, mut writer) = usb.as_ref().take_serial_0();
42
43 info!("usb initialized!");
44
45 unwrap!(
46 writer
47 .write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
48 .await
49 );
50
51 let mut buf = [0u8; 64];
52 loop {
53 let n = unwrap!(read_line(&mut reader, &mut buf).await);
54
55 for char in buf[..n].iter_mut() {
56 // upper case
57 if 0x61 <= *char && *char <= 0x7a {
58 *char &= !0x20;
59 }
60 }
61
62 unwrap!(writer.write_all(&buf[..n]).await);
63 unwrap!(writer.write_all(b"\r\n").await);
64 unwrap!(writer.flush().await);
65 }
66}