usb: add add_class
to builder, so that FooBarClass::new(&mut builder)
can set up everything.
This commit is contained in:
parent
a062baae38
commit
a2f5763a67
@ -10,3 +10,4 @@ defmt = { version = "0.3", optional = true }
|
|||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
cortex-m = "0.7.3"
|
cortex-m = "0.7.3"
|
||||||
num-traits = { version = "0.2.14", default-features = false }
|
num-traits = { version = "0.2.14", default-features = false }
|
||||||
|
heapless = "0.7.10"
|
@ -1,8 +1,11 @@
|
|||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
use super::class::UsbClass;
|
use super::class::UsbClass;
|
||||||
use super::descriptor::{BosWriter, DescriptorWriter};
|
use super::descriptor::{BosWriter, DescriptorWriter};
|
||||||
use super::driver::{Driver, EndpointAllocError};
|
use super::driver::{Driver, EndpointAllocError};
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
use super::UsbDevice;
|
use super::UsbDevice;
|
||||||
|
use super::MAX_CLASS_COUNT;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -116,6 +119,7 @@ impl<'a> Config<'a> {
|
|||||||
/// Used to build new [`UsbDevice`]s.
|
/// Used to build new [`UsbDevice`]s.
|
||||||
pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
|
pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
|
||||||
config: Config<'d>,
|
config: Config<'d>,
|
||||||
|
classes: Vec<&'d mut dyn UsbClass, MAX_CLASS_COUNT>,
|
||||||
|
|
||||||
bus: D,
|
bus: D,
|
||||||
next_interface_number: u8,
|
next_interface_number: u8,
|
||||||
@ -165,6 +169,7 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
|||||||
UsbDeviceBuilder {
|
UsbDeviceBuilder {
|
||||||
bus,
|
bus,
|
||||||
config,
|
config,
|
||||||
|
classes: Vec::new(),
|
||||||
next_interface_number: 0,
|
next_interface_number: 0,
|
||||||
next_string_index: 4,
|
next_string_index: 4,
|
||||||
|
|
||||||
@ -175,7 +180,7 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
|
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
|
||||||
pub fn build(mut self, classes: &'d mut [&'d mut dyn UsbClass]) -> UsbDevice<'d, D> {
|
pub fn build(mut self) -> UsbDevice<'d, D> {
|
||||||
self.config_descriptor.end_configuration();
|
self.config_descriptor.end_configuration();
|
||||||
self.bos_descriptor.end_bos();
|
self.bos_descriptor.end_bos();
|
||||||
|
|
||||||
@ -185,10 +190,16 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
|||||||
self.device_descriptor.into_buf(),
|
self.device_descriptor.into_buf(),
|
||||||
self.config_descriptor.into_buf(),
|
self.config_descriptor.into_buf(),
|
||||||
self.bos_descriptor.writer.into_buf(),
|
self.bos_descriptor.writer.into_buf(),
|
||||||
classes,
|
self.classes,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_class(&mut self, class: &'d mut dyn UsbClass) {
|
||||||
|
if self.classes.push(class).is_err() {
|
||||||
|
panic!("max class count reached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocates a new interface number.
|
/// Allocates a new interface number.
|
||||||
pub fn alloc_interface(&mut self) -> InterfaceNumber {
|
pub fn alloc_interface(&mut self) -> InterfaceNumber {
|
||||||
let number = self.next_interface_number;
|
let number = self.next_interface_number;
|
||||||
|
@ -14,6 +14,7 @@ pub mod types;
|
|||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use class::ControlInRequestStatus;
|
use class::ControlInRequestStatus;
|
||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
use self::class::{RequestStatus, UsbClass};
|
use self::class::{RequestStatus, UsbClass};
|
||||||
use self::control::*;
|
use self::control::*;
|
||||||
@ -53,6 +54,8 @@ pub const CONFIGURATION_VALUE: u8 = 1;
|
|||||||
/// The default value for bAlternateSetting for all interfaces.
|
/// The default value for bAlternateSetting for all interfaces.
|
||||||
pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
|
pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
|
||||||
|
|
||||||
|
pub const MAX_CLASS_COUNT: usize = 4;
|
||||||
|
|
||||||
pub struct UsbDevice<'d, D: Driver<'d>> {
|
pub struct UsbDevice<'d, D: Driver<'d>> {
|
||||||
bus: D::Bus,
|
bus: D::Bus,
|
||||||
control: D::ControlPipe,
|
control: D::ControlPipe,
|
||||||
@ -67,7 +70,7 @@ pub struct UsbDevice<'d, D: Driver<'d>> {
|
|||||||
self_powered: bool,
|
self_powered: bool,
|
||||||
pending_address: u8,
|
pending_address: u8,
|
||||||
|
|
||||||
classes: &'d mut [&'d mut dyn UsbClass],
|
classes: Vec<&'d mut dyn UsbClass, MAX_CLASS_COUNT>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||||
@ -77,7 +80,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
|||||||
device_descriptor: &'d [u8],
|
device_descriptor: &'d [u8],
|
||||||
config_descriptor: &'d [u8],
|
config_descriptor: &'d [u8],
|
||||||
bos_descriptor: &'d [u8],
|
bos_descriptor: &'d [u8],
|
||||||
classes: &'d mut [&'d mut dyn UsbClass],
|
classes: Vec<&'d mut dyn UsbClass, MAX_CLASS_COUNT>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let control = driver
|
let control = driver
|
||||||
.alloc_control_pipe(config.max_packet_size_0 as u16)
|
.alloc_control_pipe(config.max_packet_size_0 as u16)
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use core::mem;
|
use core::cell::{Cell, UnsafeCell};
|
||||||
|
use core::mem::{self, MaybeUninit};
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use defmt::info;
|
use defmt::info;
|
||||||
|
use embassy::blocking_mutex::CriticalSectionMutex;
|
||||||
use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass};
|
use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass};
|
||||||
use embassy_usb::control::{self, Request};
|
use embassy_usb::control::{self, Request};
|
||||||
use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError};
|
use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError};
|
||||||
@ -25,6 +28,18 @@ const REQ_SET_LINE_CODING: u8 = 0x20;
|
|||||||
const REQ_GET_LINE_CODING: u8 = 0x21;
|
const REQ_GET_LINE_CODING: u8 = 0x21;
|
||||||
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
|
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
control: MaybeUninit<Control>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
control: MaybeUninit::uninit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Packet level implementation of a CDC-ACM serial port.
|
/// Packet level implementation of a CDC-ACM serial port.
|
||||||
///
|
///
|
||||||
/// This class can be used directly and it has the least overhead due to directly reading and
|
/// This class can be used directly and it has the least overhead due to directly reading and
|
||||||
@ -45,21 +60,32 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> {
|
|||||||
pub data_if: InterfaceNumber,
|
pub data_if: InterfaceNumber,
|
||||||
pub read_ep: D::EndpointOut,
|
pub read_ep: D::EndpointOut,
|
||||||
pub write_ep: D::EndpointIn,
|
pub write_ep: D::EndpointIn,
|
||||||
pub control: CdcAcmControl,
|
control: &'d ControlShared,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CdcAcmControl {
|
struct Control {
|
||||||
pub comm_if: InterfaceNumber,
|
comm_if: InterfaceNumber,
|
||||||
pub line_coding: LineCoding,
|
shared: UnsafeCell<ControlShared>,
|
||||||
pub dtr: bool,
|
|
||||||
pub rts: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsbClass for CdcAcmControl {
|
/// Shared data between Control and CdcAcmClass
|
||||||
|
struct ControlShared {
|
||||||
|
line_coding: CriticalSectionMutex<Cell<LineCoding>>,
|
||||||
|
dtr: AtomicBool,
|
||||||
|
rts: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Control {
|
||||||
|
fn shared(&mut self) -> &ControlShared {
|
||||||
|
unsafe { &*(self.shared.get() as *const _) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl UsbClass for Control {
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.line_coding = LineCoding::default();
|
let shared = self.shared();
|
||||||
self.dtr = false;
|
shared.line_coding.lock(|x| x.set(LineCoding::default()));
|
||||||
self.rts = false;
|
shared.dtr.store(false, Ordering::Relaxed);
|
||||||
|
shared.rts.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn control_out(&mut self, req: control::Request, data: &[u8]) -> RequestStatus {
|
fn control_out(&mut self, req: control::Request, data: &[u8]) -> RequestStatus {
|
||||||
@ -77,18 +103,25 @@ impl UsbClass for CdcAcmControl {
|
|||||||
RequestStatus::Accepted
|
RequestStatus::Accepted
|
||||||
}
|
}
|
||||||
REQ_SET_LINE_CODING if data.len() >= 7 => {
|
REQ_SET_LINE_CODING if data.len() >= 7 => {
|
||||||
self.line_coding.data_rate = u32::from_le_bytes(data[0..4].try_into().unwrap());
|
let coding = LineCoding {
|
||||||
self.line_coding.stop_bits = data[4].into();
|
data_rate: u32::from_le_bytes(data[0..4].try_into().unwrap()),
|
||||||
self.line_coding.parity_type = data[5].into();
|
stop_bits: data[4].into(),
|
||||||
self.line_coding.data_bits = data[6];
|
parity_type: data[5].into(),
|
||||||
info!("Set line coding to: {:?}", self.line_coding);
|
data_bits: data[6],
|
||||||
|
};
|
||||||
|
self.shared().line_coding.lock(|x| x.set(coding));
|
||||||
|
info!("Set line coding to: {:?}", coding);
|
||||||
|
|
||||||
RequestStatus::Accepted
|
RequestStatus::Accepted
|
||||||
}
|
}
|
||||||
REQ_SET_CONTROL_LINE_STATE => {
|
REQ_SET_CONTROL_LINE_STATE => {
|
||||||
self.dtr = (req.value & 0x0001) != 0;
|
let dtr = (req.value & 0x0001) != 0;
|
||||||
self.rts = (req.value & 0x0002) != 0;
|
let rts = (req.value & 0x0002) != 0;
|
||||||
info!("Set dtr {}, rts {}", self.dtr, self.rts);
|
|
||||||
|
let shared = self.shared();
|
||||||
|
shared.dtr.store(dtr, Ordering::Relaxed);
|
||||||
|
shared.rts.store(rts, Ordering::Relaxed);
|
||||||
|
info!("Set dtr {}, rts {}", dtr, rts);
|
||||||
|
|
||||||
RequestStatus::Accepted
|
RequestStatus::Accepted
|
||||||
}
|
}
|
||||||
@ -112,11 +145,12 @@ impl UsbClass for CdcAcmControl {
|
|||||||
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
|
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
|
||||||
REQ_GET_LINE_CODING if req.length == 7 => {
|
REQ_GET_LINE_CODING if req.length == 7 => {
|
||||||
info!("Sending line coding");
|
info!("Sending line coding");
|
||||||
|
let coding = self.shared().line_coding.lock(|x| x.get());
|
||||||
let mut data = [0; 7];
|
let mut data = [0; 7];
|
||||||
data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
|
data[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());
|
||||||
data[4] = self.line_coding.stop_bits as u8;
|
data[4] = coding.stop_bits as u8;
|
||||||
data[5] = self.line_coding.parity_type as u8;
|
data[5] = coding.parity_type as u8;
|
||||||
data[6] = self.line_coding.data_bits;
|
data[6] = coding.data_bits;
|
||||||
control.accept(&data)
|
control.accept(&data)
|
||||||
}
|
}
|
||||||
_ => control.reject(),
|
_ => control.reject(),
|
||||||
@ -127,7 +161,11 @@ impl UsbClass for CdcAcmControl {
|
|||||||
impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||||
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
|
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
|
||||||
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
|
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
|
||||||
pub fn new(builder: &mut UsbDeviceBuilder<'d, D>, max_packet_size: u16) -> Self {
|
pub fn new(
|
||||||
|
builder: &mut UsbDeviceBuilder<'d, D>,
|
||||||
|
state: &'d mut State,
|
||||||
|
max_packet_size: u16,
|
||||||
|
) -> Self {
|
||||||
let comm_if = builder.alloc_interface();
|
let comm_if = builder.alloc_interface();
|
||||||
let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255);
|
let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255);
|
||||||
let data_if = builder.alloc_interface();
|
let data_if = builder.alloc_interface();
|
||||||
@ -207,22 +245,29 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
|||||||
builder.config_descriptor.endpoint(write_ep.info()).unwrap();
|
builder.config_descriptor.endpoint(write_ep.info()).unwrap();
|
||||||
builder.config_descriptor.endpoint(read_ep.info()).unwrap();
|
builder.config_descriptor.endpoint(read_ep.info()).unwrap();
|
||||||
|
|
||||||
|
let control = state.control.write(Control {
|
||||||
|
comm_if,
|
||||||
|
shared: UnsafeCell::new(ControlShared {
|
||||||
|
dtr: AtomicBool::new(false),
|
||||||
|
rts: AtomicBool::new(false),
|
||||||
|
line_coding: CriticalSectionMutex::new(Cell::new(LineCoding {
|
||||||
|
stop_bits: StopBits::One,
|
||||||
|
data_bits: 8,
|
||||||
|
parity_type: ParityType::None,
|
||||||
|
data_rate: 8_000,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
let control_shared = unsafe { &*(control.shared.get() as *const _) };
|
||||||
|
builder.add_class(control);
|
||||||
|
|
||||||
CdcAcmClass {
|
CdcAcmClass {
|
||||||
comm_ep,
|
comm_ep,
|
||||||
data_if,
|
data_if,
|
||||||
read_ep,
|
read_ep,
|
||||||
write_ep,
|
write_ep,
|
||||||
control: CdcAcmControl {
|
control: control_shared,
|
||||||
comm_if,
|
|
||||||
dtr: false,
|
|
||||||
rts: false,
|
|
||||||
line_coding: LineCoding {
|
|
||||||
stop_bits: StopBits::One,
|
|
||||||
data_bits: 8,
|
|
||||||
parity_type: ParityType::None,
|
|
||||||
data_rate: 8_000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,18 +279,18 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
|||||||
|
|
||||||
/// Gets the current line coding. The line coding contains information that's mainly relevant
|
/// Gets the current line coding. The line coding contains information that's mainly relevant
|
||||||
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
||||||
pub fn line_coding(&self) -> &LineCoding {
|
pub fn line_coding(&self) -> LineCoding {
|
||||||
&self.control.line_coding
|
self.control.line_coding.lock(|x| x.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the DTR (data terminal ready) state
|
/// Gets the DTR (data terminal ready) state
|
||||||
pub fn dtr(&self) -> bool {
|
pub fn dtr(&self) -> bool {
|
||||||
self.control.dtr
|
self.control.dtr.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the RTS (request to send) state
|
/// Gets the RTS (request to send) state
|
||||||
pub fn rts(&self) -> bool {
|
pub fn rts(&self) -> bool {
|
||||||
self.control.rts
|
self.control.rts.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a single packet into the IN endpoint.
|
/// Writes a single packet into the IN endpoint.
|
||||||
@ -264,88 +309,6 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
|
|
||||||
fn get_configuration_descriptors(&self, builder.config_descriptor: &mut Descriptorbuilder.config_descriptor) -> Result<()> {
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.line_coding = LineCoding::default();
|
|
||||||
self.dtr = false;
|
|
||||||
self.rts = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_in(&mut self, xfer: ControlIn<B>) {
|
|
||||||
let req = xfer.request();
|
|
||||||
|
|
||||||
if !(req.request_type == control::RequestType::Class
|
|
||||||
&& req.recipient == control::Recipient::Interface
|
|
||||||
&& req.index == u8::from(self.comm_if) as u16)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match req.request {
|
|
||||||
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
|
|
||||||
REQ_GET_LINE_CODING if req.length == 7 => {
|
|
||||||
xfer.accept(|data| {
|
|
||||||
data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
|
|
||||||
data[4] = self.line_coding.stop_bits as u8;
|
|
||||||
data[5] = self.line_coding.parity_type as u8;
|
|
||||||
data[6] = self.line_coding.data_bits;
|
|
||||||
|
|
||||||
Ok(7)
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
xfer.reject().ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_out(&mut self, xfer: ControlOut<B>) {
|
|
||||||
let req = xfer.request();
|
|
||||||
|
|
||||||
if !(req.request_type == control::RequestType::Class
|
|
||||||
&& req.recipient == control::Recipient::Interface
|
|
||||||
&& req.index == u8::from(self.comm_if) as u16)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match req.request {
|
|
||||||
REQ_SEND_ENCAPSULATED_COMMAND => {
|
|
||||||
// We don't actually support encapsulated commands but pretend we do for standards
|
|
||||||
// compatibility.
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
|
|
||||||
self.line_coding.data_rate =
|
|
||||||
u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
|
|
||||||
self.line_coding.stop_bits = xfer.data()[4].into();
|
|
||||||
self.line_coding.parity_type = xfer.data()[5].into();
|
|
||||||
self.line_coding.data_bits = xfer.data()[6];
|
|
||||||
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
REQ_SET_CONTROL_LINE_STATE => {
|
|
||||||
self.dtr = (req.value & 0x0001) != 0;
|
|
||||||
self.rts = (req.value & 0x0002) != 0;
|
|
||||||
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
xfer.reject().ok();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// Number of stop bits for LineCoding
|
/// Number of stop bits for LineCoding
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
|
#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
|
||||||
pub enum StopBits {
|
pub enum StopBits {
|
||||||
@ -393,7 +356,7 @@ impl From<u8> for ParityType {
|
|||||||
///
|
///
|
||||||
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
|
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
|
||||||
/// be ignored if you don't plan to interface with a physical UART.
|
/// be ignored if you don't plan to interface with a physical UART.
|
||||||
#[derive(defmt::Format)]
|
#[derive(Clone, Copy, defmt::Format)]
|
||||||
pub struct LineCoding {
|
pub struct LineCoding {
|
||||||
stop_bits: StopBits,
|
stop_bits: StopBits,
|
||||||
data_bits: u8,
|
data_bits: u8,
|
||||||
|
@ -16,12 +16,11 @@ use embassy_nrf::interrupt;
|
|||||||
use embassy_nrf::pac;
|
use embassy_nrf::pac;
|
||||||
use embassy_nrf::usb::Driver;
|
use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::Peripherals;
|
use embassy_nrf::Peripherals;
|
||||||
use embassy_usb::class::UsbClass;
|
|
||||||
use embassy_usb::driver::{EndpointIn, EndpointOut};
|
use embassy_usb::driver::{EndpointIn, EndpointOut};
|
||||||
use embassy_usb::{Config, UsbDeviceBuilder};
|
use embassy_usb::{Config, UsbDeviceBuilder};
|
||||||
use futures::future::join3;
|
use futures::future::join3;
|
||||||
|
|
||||||
use crate::cdc_acm::CdcAcmClass;
|
use crate::cdc_acm::{CdcAcmClass, State};
|
||||||
|
|
||||||
#[embassy::main]
|
#[embassy::main]
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
@ -48,6 +47,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
|||||||
let mut device_descriptor = [0; 256];
|
let mut device_descriptor = [0; 256];
|
||||||
let mut config_descriptor = [0; 256];
|
let mut config_descriptor = [0; 256];
|
||||||
let mut bos_descriptor = [0; 256];
|
let mut bos_descriptor = [0; 256];
|
||||||
|
|
||||||
|
let mut state = State::new();
|
||||||
|
|
||||||
let mut builder = UsbDeviceBuilder::new(
|
let mut builder = UsbDeviceBuilder::new(
|
||||||
driver,
|
driver,
|
||||||
config,
|
config,
|
||||||
@ -57,11 +59,10 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create classes on the builder.
|
// Create classes on the builder.
|
||||||
let mut class = CdcAcmClass::new(&mut builder, 64);
|
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
|
||||||
|
|
||||||
// Build the builder.
|
// Build the builder.
|
||||||
let mut classes: [&mut dyn UsbClass; 1] = [&mut class.control];
|
let mut usb = builder.build();
|
||||||
let mut usb = builder.build(&mut classes);
|
|
||||||
|
|
||||||
// Run the USB device.
|
// Run the USB device.
|
||||||
let fut1 = usb.run();
|
let fut1 = usb.run();
|
||||||
|
Loading…
Reference in New Issue
Block a user