11//! Firmware for the nRF52840 Dongle, for playing the puzzle game
22//!
33//! Sets up a USB Serial port and listens for radio packets.
4+ //!
5+ //! This application has two queues:
6+ //!
7+ //! * USB HID from host computer -> `usb_hid` task -> `HidTransferHandler` -> `MSG_CHANNEL` -> `radio` task
8+ //! * USB ACM from host computer -> `usb_acm` task -> `MSG_CHANNEL` -> `radio` task
9+ //! * various tasks -> `ACM_PIPE` - `usb_acm` task -> USB ACM to host computer
410
511#![ no_main]
612#![ no_std]
13+ #![ deny( missing_docs) ]
714
815#[ cfg( not( feature = "dk" ) ) ]
916use bsp:: RgbLed ;
@@ -22,10 +29,27 @@ mod app {
2229 use rtic_monotonics:: systick:: prelude:: * ;
2330 use static_cell:: StaticCell ;
2431
25- const MSG_QUEUE_LEN : usize = 8 ;
26- const ACM_QUEUE_LEN : usize = 256 ;
32+ const MSG_CHANNEL_LEN : usize = 8 ;
33+ const ACM_PIPE_LEN : usize = 256 ;
2734 const MAX_ACM_PACKET_SIZE : usize = 64 ;
2835
36+ /// Handles commands from host, to application
37+ type MessageChannel =
38+ embassy_sync:: channel:: Channel < CriticalSectionRawMutex , Message , MSG_CHANNEL_LEN > ;
39+ /// The receiving end of a [`MessageChannel`]
40+ type MessageChannelReceiver =
41+ embassy_sync:: channel:: Receiver < ' static , CriticalSectionRawMutex , Message , MSG_CHANNEL_LEN > ;
42+ /// The sending end of a [`MessageChannel`]
43+ type MessageChannelSender =
44+ embassy_sync:: channel:: Sender < ' static , CriticalSectionRawMutex , Message , MSG_CHANNEL_LEN > ;
45+
46+ /// Handles text output from application, to host
47+ type AcmPipe = embassy_sync:: pipe:: Pipe < CriticalSectionRawMutex , ACM_PIPE_LEN > ;
48+ /// The reading end of an [`AcmPipe`]
49+ type AcmPipeReader = embassy_sync:: pipe:: Reader < ' static , CriticalSectionRawMutex , ACM_PIPE_LEN > ;
50+ /// The writing end of an [`AcmPipe`]
51+ type AcmPipeWriter = embassy_sync:: pipe:: Writer < ' static , CriticalSectionRawMutex , ACM_PIPE_LEN > ;
52+
2953 /// The secret message, but encoded.
3054 ///
3155 /// We do this rather than the plaintext -- otherwise `strings $elf` will reveal the answer
@@ -57,33 +81,32 @@ mod app {
5781 // Intermediate buffer which is required because we can not used async code
5882 // in the [core::fmt::Write] implementation.
5983 buffer : heapless:: String < 256 > ,
60- usb_acm_writer : embassy_sync :: pipe :: Writer < ' static , CriticalSectionRawMutex , ACM_QUEUE_LEN > ,
84+ acm_pipe_writer : AcmPipeWriter ,
6185 }
6286
6387 impl core:: fmt:: Write for WriteAsyncPipeAdapter {
6488 fn write_str ( & mut self , s : & str ) -> core:: fmt:: Result {
65- write ! ( self . buffer, "{}" , s) . unwrap ( ) ;
66- Ok ( ( ) )
89+ write ! ( self . buffer, "{}" , s)
6790 }
6891 }
6992
7093 impl WriteAsyncPipeAdapter {
7194 /// Flush the buffer to the underlying writer.
7295 async fn flush ( & mut self ) {
73- self . usb_acm_writer . write_all ( self . buffer . as_bytes ( ) ) . await ;
96+ self . acm_pipe_writer . write_all ( self . buffer . as_bytes ( ) ) . await ;
7497 self . buffer . clear ( ) ;
7598 }
7699 }
77100
101+ /// Messages we can get over USB HID which the radio task needs to handle
78102 #[ derive( Debug , defmt:: Format , Copy , Clone , PartialEq , Eq ) ]
79103 enum Message {
80104 ChangeChannel ( u8 ) ,
81105 WantInfo ,
82106 }
83107
84- struct HidTransferHandler (
85- embassy_sync:: channel:: Sender < ' static , CriticalSectionRawMutex , Message , MSG_QUEUE_LEN > ,
86- ) ;
108+ /// A helper for dealing with incoming USB HID events
109+ struct HidTransferHandler ( MessageChannelSender ) ;
87110
88111 impl hid:: RequestHandler for HidTransferHandler {
89112 // HID requests are used to switch the channel.
@@ -115,20 +138,19 @@ mod app {
115138 rx_count : u32 ,
116139 /// How many packets have been received with errors?
117140 err_count : u32 ,
118- /// A place to read the message queue
119- msg_queue_rx : embassy_sync:: channel:: Receiver <
120- ' static ,
121- CriticalSectionRawMutex ,
122- Message ,
123- MSG_QUEUE_LEN ,
124- > ,
125- /// A place to write to the message queue
126- msg_queue_tx_acm :
127- embassy_sync:: channel:: Sender < ' static , CriticalSectionRawMutex , Message , MSG_QUEUE_LEN > ,
141+ /// A place to read from the message channel
142+ msg_channel_receiver : MessageChannelReceiver ,
143+ /// A place to write to the message channel
144+ msg_channel_sender_acm : MessageChannelSender ,
145+ /// The LED on the board
128146 leds : bsp:: Leds ,
147+ /// Our raw USB device
129148 usb_dev : embassy_usb:: UsbDevice < ' static , hal:: usb:: Driver < ' static , HardwareVbusDetect > > ,
130- usb_acm_write_adapter : WriteAsyncPipeAdapter ,
131- usb_acm_reader : embassy_sync:: pipe:: Reader < ' static , CriticalSectionRawMutex , ACM_QUEUE_LEN > ,
149+ /// Handles doing async writeln! to the USB ACM interface
150+ usb_acm_pipe_adapter : WriteAsyncPipeAdapter ,
151+ /// Provides queued data to be written to USB ACM
152+ acm_pipe_reader : AcmPipeReader ,
153+ /// The ACM part of our USB device interface
132154 usb_acm : embassy_usb:: class:: cdc_acm:: CdcAcmClass <
133155 ' static ,
134156 hal:: usb:: Driver < ' static , HardwareVbusDetect > ,
@@ -249,19 +271,20 @@ mod app {
249271 #[ cfg( not( feature = "dk" ) ) ]
250272 radio. set_channel ( current_channel) ;
251273
252- static MSG_QUEUE : static_cell:: ConstStaticCell <
253- embassy_sync:: channel:: Channel < CriticalSectionRawMutex , Message , MSG_QUEUE_LEN > ,
254- > = static_cell:: ConstStaticCell :: new ( embassy_sync:: channel:: Channel :: new ( ) ) ;
255- static ACM_QUEUE : static_cell:: ConstStaticCell <
256- embassy_sync:: pipe:: Pipe < CriticalSectionRawMutex , ACM_QUEUE_LEN > ,
257- > = static_cell:: ConstStaticCell :: new ( embassy_sync:: pipe:: Pipe :: new ( ) ) ;
274+ static MSG_CHANNEL : static_cell:: ConstStaticCell < MessageChannel > =
275+ static_cell:: ConstStaticCell :: new ( embassy_sync:: channel:: Channel :: new ( ) ) ;
276+ static ACM_PIPE : static_cell:: ConstStaticCell < AcmPipe > =
277+ static_cell:: ConstStaticCell :: new ( embassy_sync:: pipe:: Pipe :: new ( ) ) ;
258278
259- let msg_queue = MSG_QUEUE . take ( ) ;
260- let ( acm_reader, acm_writer) = ACM_QUEUE . take ( ) . split ( ) ;
279+ let msg_channel = MSG_CHANNEL . take ( ) ;
280+ let msg_channel_receiver = msg_channel. receiver ( ) ;
281+ let msg_channel_sender_acm = msg_channel. sender ( ) ;
282+ let msg_channel_sender_hid = msg_channel. sender ( ) ;
283+ let ( acm_pipe_reader, acm_pipe_writer) = ACM_PIPE . take ( ) . split ( ) ;
261284
262- let acm_adapter = WriteAsyncPipeAdapter {
285+ let usb_acm_pipe_adapter = WriteAsyncPipeAdapter {
263286 buffer : heapless:: String :: new ( ) ,
264- usb_acm_writer : acm_writer ,
287+ acm_pipe_writer ,
265288 } ;
266289
267290 defmt:: debug!( "Building structures..." ) ;
@@ -273,18 +296,18 @@ mod app {
273296 timer : board. timer ,
274297 rx_count : 0 ,
275298 err_count : 0 ,
276- msg_queue_rx : msg_queue . receiver ( ) ,
277- msg_queue_tx_acm : msg_queue . sender ( ) ,
299+ msg_channel_receiver ,
300+ msg_channel_sender_acm ,
278301 leds : board. leds ,
279302 usb_dev,
280303 usb_acm,
281- usb_acm_write_adapter : acm_adapter ,
282- usb_acm_reader : acm_reader ,
304+ usb_acm_pipe_adapter ,
305+ acm_pipe_reader ,
283306 } ;
284307
285308 usb_dev:: spawn ( ) . unwrap ( ) ;
286309 usb_acm:: spawn ( ) . unwrap ( ) ;
287- let _ = usb_hid:: spawn ( hid_reader, msg_queue . sender ( ) ) ;
310+ let _ = usb_hid:: spawn ( hid_reader, msg_channel_sender_hid ) ;
288311 radio:: spawn ( ) . unwrap ( ) ;
289312
290313 defmt:: debug!( "Init Complete!" ) ;
@@ -312,28 +335,34 @@ mod app {
312335 }
313336 }
314337
338+ /// Run the USB Device
315339 #[ task( local = [ usb_dev] , priority = 1 ) ]
316340 async fn usb_dev ( ctx : usb_dev:: Context ) {
317341 ctx. local . usb_dev . run ( ) . await ;
318342 }
319343
344+ /// Handles USB HID data
320345 #[ task( priority = 1 ) ]
321346 async fn usb_hid (
322347 _ctx : usb_hid:: Context ,
323348 // Need to send this by value, because it is consumed by the run method.
324349 usb_hid_reader : hid:: HidReader < ' static , hal:: usb:: Driver < ' static , HardwareVbusDetect > , 64 > ,
325- msg_queue_tx_hid : embassy_sync:: channel:: Sender <
326- ' static ,
327- CriticalSectionRawMutex ,
328- Message ,
329- MSG_QUEUE_LEN ,
330- > ,
350+ msg_channel_tx_hid : MessageChannelSender ,
331351 ) {
332- let mut req_handler = HidTransferHandler ( msg_queue_tx_hid ) ;
352+ let mut req_handler = HidTransferHandler ( msg_channel_tx_hid ) ;
333353 usb_hid_reader. run ( false , & mut req_handler) . await ;
334354 }
335355
336- #[ task( local = [ usb_acm, msg_queue_tx_acm, usb_acm_reader] , priority = 1 ) ]
356+ /// This task handles the USB ACM interface
357+ ///
358+ /// * Puts messages into the MSG_CHANNEL (via `msg_channel_sender_acm`) when
359+ /// text received from the USB ACM interface
360+ /// * Transfers text into the USB ACM interface, from the ACM_PIPE (via
361+ /// `acm_pipe_reader`)
362+ /// * Deals with being disconnected from the host
363+ ///
364+ /// Defers to [`connected_usb_acm`] for most of the work
365+ #[ task( local = [ usb_acm, msg_channel_sender_acm, acm_pipe_reader] , priority = 1 ) ]
337366 async fn usb_acm ( mut ctx : usb_acm:: Context ) {
338367 loop {
339368 // Wait for up to 200 ms for a connection, discard ACM data otherwise.
@@ -344,12 +373,21 @@ mod app {
344373 Err ( _) => {
345374 let mut dummy_buf: [ u8 ; 32 ] = [ 0 ; 32 ] ;
346375 // Timeout. Consume the message queue.
347- while let Ok ( _bytes_read) = ctx. local . usb_acm_reader . try_read ( & mut dummy_buf) { }
376+ while let Ok ( _bytes_read) = ctx. local . acm_pipe_reader . try_read ( & mut dummy_buf) {
377+ }
348378 }
349379 }
350380 }
351381 }
352382
383+ /// This task handles the USB ACM interface
384+ ///
385+ /// * Puts messages into the MSG_CHANNEL (via `msg_channel_sender_acm`) when
386+ /// text received from the USB ACM interface
387+ /// * Transfers text into the USB ACM interface, from the ACM_PIPE (via
388+ /// `acm_pipe_reader`)
389+ ///
390+ /// Called by [`usb_acm`] when we are actually connected
353391 async fn connected_usb_acm ( ctx : & mut usb_acm:: Context < ' _ > ) {
354392 let mut buffer = [ 0u8 ; MAX_ACM_PACKET_SIZE ] ;
355393 loop {
@@ -363,7 +401,11 @@ mod app {
363401 for b in & buffer[ 0 ..n] {
364402 if * b == b'?' {
365403 // User pressed "?" in the terminal
366- _ = ctx. local . msg_queue_tx_acm . send ( Message :: WantInfo ) . await ;
404+ _ = ctx
405+ . local
406+ . msg_channel_sender_acm
407+ . send ( Message :: WantInfo )
408+ . await ;
367409 }
368410 }
369411 }
@@ -378,7 +420,7 @@ mod app {
378420 }
379421 while let Ok ( bytes_read) = ctx
380422 . local
381- . usb_acm_reader
423+ . acm_pipe_reader
382424 . try_read ( & mut buffer[ 0 ..MAX_ACM_PACKET_SIZE - 1 ] )
383425 {
384426 match Mono :: timeout_after (
@@ -405,23 +447,31 @@ mod app {
405447 }
406448 }
407449
450+ /// Commands we can receive over the radio
408451 enum Command {
409452 SendSecret ,
410453 MapChar ( u8 , u8 ) ,
411454 Correct ,
412455 Wrong ,
413456 }
414457
458+ /// Handles the radio interface
459+ ///
460+ /// * Listens for incoming data
461+ /// * Works out what command it is
462+ /// * Sends the appropriate response
463+ /// * Handles messages on the MSG_CHANNEL (via `msg_channel_receiver`)
464+ /// * Sends logs to the ACM_PIPE (via `usb_acm_pipe_adapter`)
415465 #[ task( local = [
416466 radio,
417467 current_channel,
418468 packet,
419469 timer,
420470 rx_count,
421471 err_count,
422- msg_queue_rx ,
472+ msg_channel_receiver ,
423473 leds,
424- usb_acm_write_adapter ,
474+ usb_acm_pipe_adapter ,
425475 ] , priority = 2 ) ]
426476 async fn radio ( mut ctx : radio:: Context ) {
427477 defmt:: info!(
@@ -442,7 +492,7 @@ mod app {
442492 }
443493
444494 loop {
445- while let Ok ( msg) = ctx. local . msg_queue_rx . try_receive ( ) {
495+ while let Ok ( msg) = ctx. local . msg_channel_receiver . try_receive ( ) {
446496 match msg {
447497 Message :: WantInfo => {
448498 defmt:: info!(
@@ -452,20 +502,20 @@ mod app {
452502 ctx. local. current_channel
453503 ) ;
454504 let _ = writeln ! (
455- & mut ctx. local. usb_acm_write_adapter ,
505+ & mut ctx. local. usb_acm_pipe_adapter ,
456506 "\n rx={}, err={}, ch={}, app=puzzle-fw" ,
457507 ctx. local. rx_count, ctx. local. err_count, ctx. local. current_channel
458508 ) ;
459- ctx. local . usb_acm_write_adapter . flush ( ) . await ;
509+ ctx. local . usb_acm_pipe_adapter . flush ( ) . await ;
460510 }
461511 Message :: ChangeChannel ( n) => {
462512 defmt:: info!( "Changing Channel to {}" , n) ;
463513 let _ = writeln ! (
464- & mut ctx. local. usb_acm_write_adapter ,
514+ & mut ctx. local. usb_acm_pipe_adapter ,
465515 "\n Changing Channel to {}" ,
466516 n
467517 ) ;
468- ctx. local . usb_acm_write_adapter . flush ( ) . await ;
518+ ctx. local . usb_acm_pipe_adapter . flush ( ) . await ;
469519
470520 if !( 11 ..=26 ) . contains ( & n) {
471521 defmt:: info!( "Bad Channel {}!" , n) ;
@@ -510,7 +560,7 @@ mod app {
510560 * dest = * src;
511561 }
512562
513- let _ = writeln ! ( & mut ctx. local. usb_acm_write_adapter , "TX Secret" ) ;
563+ let _ = writeln ! ( & mut ctx. local. usb_acm_pipe_adapter , "TX Secret" ) ;
514564 #[ cfg( not( feature = "dk" ) ) ]
515565 {
516566 ctx. local . leds . ld2_rgb . blue . on ( ) ;
@@ -522,7 +572,7 @@ mod app {
522572 ctx. local . packet . set_len ( 1 + ADDR_BYTES as u8 ) ;
523573 ctx. local . packet [ ADDR_BYTES ] = cipher;
524574 let _ = writeln ! (
525- & mut ctx. local. usb_acm_write_adapter ,
575+ & mut ctx. local. usb_acm_pipe_adapter ,
526576 "TX Map({plain}) => {cipher}"
527577 ) ;
528578 #[ cfg( not( feature = "dk" ) ) ]
@@ -542,8 +592,7 @@ mod app {
542592 {
543593 * dest = * src;
544594 }
545- let _ =
546- writeln ! ( & mut ctx. local. usb_acm_write_adapter, "TX Correct" ) ;
595+ let _ = writeln ! ( & mut ctx. local. usb_acm_pipe_adapter, "TX Correct" ) ;
547596 #[ cfg( not( feature = "dk" ) ) ]
548597 {
549598 ctx. local . leds . ld2_rgb . blue . on ( ) ;
@@ -562,7 +611,7 @@ mod app {
562611 * dest = * src;
563612 }
564613 let _ =
565- writeln ! ( & mut ctx. local. usb_acm_write_adapter , "TX Incorrect" ) ;
614+ writeln ! ( & mut ctx. local. usb_acm_pipe_adapter , "TX Incorrect" ) ;
566615 #[ cfg( not( feature = "dk" ) ) ]
567616 {
568617 ctx. local . leds . ld2_rgb . blue . off ( ) ;
@@ -579,20 +628,20 @@ mod app {
579628 Mono :: delay ( 500 . micros ( ) ) . await ;
580629 if let Err ( e) = ctx. local . radio . try_send ( ctx. local . packet ) . await {
581630 let _ = writeln ! (
582- & mut ctx. local. usb_acm_write_adapter ,
631+ & mut ctx. local. usb_acm_pipe_adapter ,
583632 "\n Writing reply packet failed with error {:?}" ,
584633 e
585634 ) ;
586635 }
587636 }
588- ctx. local . usb_acm_write_adapter . flush ( ) . await ;
637+ ctx. local . usb_acm_pipe_adapter . flush ( ) . await ;
589638 }
590639 Err ( _e) => {
591640 defmt:: debug!( "RX fail!" ) ;
592641 let _ = ctx
593642 . local
594- . usb_acm_write_adapter
595- . usb_acm_writer
643+ . usb_acm_pipe_adapter
644+ . acm_pipe_writer
596645 . write ( "!" . as_bytes ( ) )
597646 . await ;
598647 * ctx. local . err_count += 1 ;
0 commit comments