diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index eb460e738..8a341b8f7 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -69,6 +69,7 @@ jobs:
           builder ./embassy-futures crates/embassy-futures/git.zup
           builder ./embassy-lora crates/embassy-lora/git.zup
           builder ./embassy-net crates/embassy-net/git.zup
+          builder ./embassy-net-driver crates/embassy-net-driver/git.zup
           builder ./embassy-nrf crates/embassy-nrf/git.zup
           builder ./embassy-rp crates/embassy-rp/git.zup
           builder ./embassy-sync crates/embassy-sync/git.zup
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 6b8183f89..086f435da 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,27 +4,21 @@
   "rust-analyzer.checkOnSave.noDefaultFeatures": true,
   "rust-analyzer.cargo.noDefaultFeatures": true,
   "rust-analyzer.procMacro.enable": true,
-  //"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
-  "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
+  "rust-analyzer.cargo.target": "thumbv7em-none-eabi",
+  //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
   "rust-analyzer.cargo.features": [
-    // These are needed to prevent embassy-net from failing to build
-    //"embassy-net/medium-ethernet",
-    //"embassy-net/tcp",
-    //"time-tick-16mhz",
-    //"defmt-timestamp-uptime",
-    //"nightly",
-    //"unstable-traits",
+    "nightly",
   ],
   "rust-analyzer.linkedProjects": [
     // Declare for the target you wish to develop
-    //"embassy-executor/Cargo.toml",
-    //"embassy-sync/Cargo.toml",
-    //"examples/nrf/Cargo.toml",
+    // "embassy-executor/Cargo.toml",
+    // "embassy-sync/Cargo.toml",
+    "examples/nrf/Cargo.toml",
     // "examples/nrf-rtos-trace/Cargo.toml",
     // "examples/rp/Cargo.toml",
     // "examples/std/Cargo.toml",
     // "examples/stm32f0/Cargo.toml",
-    //"examples/stm32f1/Cargo.toml",
+    // "examples/stm32f1/Cargo.toml",
     // "examples/stm32f2/Cargo.toml",
     // "examples/stm32f3/Cargo.toml",
     // "examples/stm32f4/Cargo.toml",
@@ -35,7 +29,7 @@
     // "examples/stm32l0/Cargo.toml",
     // "examples/stm32l1/Cargo.toml",
     // "examples/stm32l4/Cargo.toml",
-    "examples/stm32l5/Cargo.toml",
+    // "examples/stm32l5/Cargo.toml",
     // "examples/stm32u5/Cargo.toml",
     // "examples/stm32wb/Cargo.toml",
     // "examples/stm32wb55/Cargo.toml",
diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml
new file mode 100644
index 000000000..7ab9d1194
--- /dev/null
+++ b/embassy-net-driver/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "embassy-net-driver"
+version = "0.1.0"
+edition = "2021"
+license = "MIT OR Apache-2.0"
+
+
+[package.metadata.embassy_docs]
+src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net/src/"
+src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/"
+features = ["defmt"]
+target = "thumbv7em-none-eabi"
+
+[dependencies]
+defmt = { version = "0.3", optional = true }
\ No newline at end of file
diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs
new file mode 100644
index 000000000..a39cfecc1
--- /dev/null
+++ b/embassy-net-driver/src/lib.rs
@@ -0,0 +1,175 @@
+#![no_std]
+
+use core::task::Context;
+
+pub trait Driver {
+    type RxToken<'a>: RxToken
+    where
+        Self: 'a;
+    type TxToken<'a>: TxToken
+    where
+        Self: 'a;
+
+    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
+    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>>;
+    fn link_state(&mut self, cx: &mut Context) -> LinkState;
+
+    fn capabilities(&self) -> Capabilities;
+    fn ethernet_address(&self) -> [u8; 6];
+}
+
+impl<T: ?Sized + Driver> Driver for &mut T {
+    type RxToken<'a> = T::RxToken<'a>
+    where
+        Self: 'a;
+    type TxToken<'a> = T::TxToken<'a>
+    where
+        Self: 'a;
+
+    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
+        T::transmit(self, cx)
+    }
+    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
+        T::receive(self, cx)
+    }
+    fn capabilities(&self) -> Capabilities {
+        T::capabilities(self)
+    }
+    fn link_state(&mut self, cx: &mut Context) -> LinkState {
+        T::link_state(self, cx)
+    }
+    fn ethernet_address(&self) -> [u8; 6] {
+        T::ethernet_address(self)
+    }
+}
+
+/// A token to receive a single network packet.
+pub trait RxToken {
+    /// Consumes the token to receive a single network packet.
+    ///
+    /// This method receives a packet and then calls the given closure `f` with the raw
+    /// packet bytes as argument.
+    fn consume<R, F>(self, f: F) -> R
+    where
+        F: FnOnce(&mut [u8]) -> R;
+}
+
+/// A token to transmit a single network packet.
+pub trait TxToken {
+    /// Consumes the token to send a single network packet.
+    ///
+    /// This method constructs a transmit buffer of size `len` and calls the passed
+    /// closure `f` with a mutable reference to that buffer. The closure should construct
+    /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure
+    /// returns, the transmit buffer is sent out.
+    fn consume<R, F>(self, len: usize, f: F) -> R
+    where
+        F: FnOnce(&mut [u8]) -> R;
+}
+
+/// A description of device capabilities.
+///
+/// Higher-level protocols may achieve higher throughput or lower latency if they consider
+/// the bandwidth or packet size limitations.
+#[derive(Debug, Clone, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[non_exhaustive]
+pub struct Capabilities {
+    /// Medium of the device.
+    ///
+    /// This indicates what kind of packet the sent/received bytes are, and determines
+    /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
+    /// for Ethernet mediums.
+    pub medium: Medium,
+
+    /// Maximum transmission unit.
+    ///
+    /// The network device is unable to send or receive frames larger than the value returned
+    /// by this function.
+    ///
+    /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but
+    /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14.
+    ///
+    /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet
+    /// devices. This is a common source of confusion.
+    ///
+    /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets.
+    pub max_transmission_unit: usize,
+
+    /// Maximum burst size, in terms of MTU.
+    ///
+    /// The network device is unable to send or receive bursts large than the value returned
+    /// by this function.
+    ///
+    /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are
+    /// dynamically allocated.
+    pub max_burst_size: Option<usize>,
+
+    /// Checksum behavior.
+    ///
+    /// If the network device is capable of verifying or computing checksums for some protocols,
+    /// it can request that the stack not do so in software to improve performance.
+    pub checksum: ChecksumCapabilities,
+}
+
+/// Type of medium of a device.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Medium {
+    /// Ethernet medium. Devices of this type send and receive Ethernet frames,
+    /// and interfaces using it must do neighbor discovery via ARP or NDISC.
+    ///
+    /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
+    Ethernet,
+
+    /// IP medium. Devices of this type send and receive IP frames, without an
+    /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
+    ///
+    /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
+    Ip,
+}
+
+impl Default for Medium {
+    fn default() -> Medium {
+        Medium::Ethernet
+    }
+}
+
+/// A description of checksum behavior for every supported protocol.
+#[derive(Debug, Clone, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[non_exhaustive]
+pub struct ChecksumCapabilities {
+    pub ipv4: Checksum,
+    pub udp: Checksum,
+    pub tcp: Checksum,
+    pub icmpv4: Checksum,
+    pub icmpv6: Checksum,
+}
+
+/// A description of checksum behavior for a particular protocol.
+#[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Checksum {
+    /// Verify checksum when receiving and compute checksum when sending.
+    Both,
+    /// Verify checksum when receiving.
+    Rx,
+    /// Compute checksum before sending.
+    Tx,
+    /// Ignore checksum completely.
+    None,
+}
+
+impl Default for Checksum {
+    fn default() -> Checksum {
+        Checksum::Both
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum LinkState {
+    Down,
+    Up,
+}
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index 357f87922..9214fd17e 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -15,7 +15,7 @@ target = "thumbv7em-none-eabi"
 default = []
 std = []
 
-defmt = ["dep:defmt", "smoltcp/defmt"]
+defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"]
 
 nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"]
 unstable-traits = []
@@ -33,6 +33,7 @@ medium-ip = ["smoltcp/medium-ip"]
 defmt = { version = "0.3", optional = true }
 log = { version = "0.4.14", optional = true }
 
+embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
 embassy-time = { version = "0.1.0", path = "../embassy-time" }
 embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
 embedded-io = { version = "0.4.0", optional = true }
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs
index 5d86ca91e..44f7dc7bd 100644
--- a/embassy-net/src/device.rs
+++ b/embassy-net/src/device.rs
@@ -1,93 +1,20 @@
 use core::task::Context;
 
+use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken};
 use smoltcp::phy;
-pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium};
 
-#[derive(PartialEq, Eq, Clone, Copy)]
-pub enum LinkState {
-    Down,
-    Up,
-}
-
-pub trait Device {
-    type RxToken<'a>: RxToken
-    where
-        Self: 'a;
-    type TxToken<'a>: TxToken
-    where
-        Self: 'a;
-
-    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
-    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>>;
-    fn link_state(&mut self, cx: &mut Context) -> LinkState;
-
-    fn capabilities(&self) -> phy::DeviceCapabilities;
-    fn ethernet_address(&self) -> [u8; 6];
-}
-
-impl<T: ?Sized + Device> Device for &mut T {
-    type RxToken<'a> = T::RxToken<'a>
-    where
-        Self: 'a;
-    type TxToken<'a> = T::TxToken<'a>
-    where
-        Self: 'a;
-
-    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
-        T::transmit(self, cx)
-    }
-    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
-        T::receive(self, cx)
-    }
-    fn capabilities(&self) -> phy::DeviceCapabilities {
-        T::capabilities(self)
-    }
-    fn link_state(&mut self, cx: &mut Context) -> LinkState {
-        T::link_state(self, cx)
-    }
-    fn ethernet_address(&self) -> [u8; 6] {
-        T::ethernet_address(self)
-    }
-}
-
-/// A token to receive a single network packet.
-pub trait RxToken {
-    /// Consumes the token to receive a single network packet.
-    ///
-    /// This method receives a packet and then calls the given closure `f` with the raw
-    /// packet bytes as argument.
-    fn consume<R, F>(self, f: F) -> R
-    where
-        F: FnOnce(&mut [u8]) -> R;
-}
-
-/// A token to transmit a single network packet.
-pub trait TxToken {
-    /// Consumes the token to send a single network packet.
-    ///
-    /// This method constructs a transmit buffer of size `len` and calls the passed
-    /// closure `f` with a mutable reference to that buffer. The closure should construct
-    /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure
-    /// returns, the transmit buffer is sent out.
-    fn consume<R, F>(self, len: usize, f: F) -> R
-    where
-        F: FnOnce(&mut [u8]) -> R;
-}
-
-///////////////////////////
-
-pub(crate) struct DeviceAdapter<'d, 'c, T>
+pub(crate) struct DriverAdapter<'d, 'c, T>
 where
-    T: Device,
+    T: Driver,
 {
     // must be Some when actually using this to rx/tx
     pub cx: Option<&'d mut Context<'c>>,
     pub inner: &'d mut T,
 }
 
-impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T>
+impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T>
 where
-    T: Device,
+    T: Driver,
 {
     type RxToken<'a> = RxTokenAdapter<T::RxToken<'a>> where Self: 'a;
     type TxToken<'a> = TxTokenAdapter<T::TxToken<'a>> where Self: 'a;
@@ -105,7 +32,39 @@ where
 
     /// Get a description of device capabilities.
     fn capabilities(&self) -> phy::DeviceCapabilities {
-        self.inner.capabilities()
+        fn convert(c: Checksum) -> phy::Checksum {
+            match c {
+                Checksum::Both => phy::Checksum::Both,
+                Checksum::Tx => phy::Checksum::Tx,
+                Checksum::Rx => phy::Checksum::Rx,
+                Checksum::None => phy::Checksum::None,
+            }
+        }
+        let caps: Capabilities = self.inner.capabilities();
+        let mut smolcaps = phy::DeviceCapabilities::default();
+
+        smolcaps.max_transmission_unit = caps.max_transmission_unit;
+        smolcaps.max_burst_size = caps.max_burst_size;
+        smolcaps.medium = match caps.medium {
+            #[cfg(feature = "medium-ethernet")]
+            Medium::Ethernet => phy::Medium::Ethernet,
+            #[cfg(feature = "medium-ip")]
+            Medium::Ip => phy::Medium::Ip,
+            _ => panic!(
+                "Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.",
+                caps.medium
+            ),
+        };
+        smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);
+        #[cfg(feature = "proto-ipv6")]
+        {
+            smolcaps.checksum.ipv6 = convert(caps.checksum.ipv6);
+        }
+        smolcaps.checksum.tcp = convert(caps.checksum.tcp);
+        smolcaps.checksum.udp = convert(caps.checksum.udp);
+        smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4);
+
+        smolcaps
     }
 }
 
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index afe0d6da0..b58c9cf36 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -18,6 +18,7 @@ use core::cell::RefCell;
 use core::future::{poll_fn, Future};
 use core::task::{Context, Poll};
 
+use embassy_net_driver::{Driver, LinkState, Medium};
 use embassy_sync::waitqueue::WakerRegistration;
 use embassy_time::{Instant, Timer};
 use futures::pin_mut;
@@ -27,8 +28,6 @@ use smoltcp::iface::SocketHandle;
 use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage};
 #[cfg(feature = "medium-ethernet")]
 use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes};
-#[cfg(feature = "medium-ethernet")]
-use smoltcp::phy::Medium;
 #[cfg(feature = "dhcpv4")]
 use smoltcp::socket::dhcpv4;
 // smoltcp reexports
@@ -41,7 +40,7 @@ pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
 #[cfg(feature = "udp")]
 pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint};
 
-use crate::device::{Device, DeviceAdapter, LinkState};
+use crate::device::DriverAdapter;
 
 const LOCAL_PORT_MIN: u16 = 1025;
 const LOCAL_PORT_MAX: u16 = 65535;
@@ -82,12 +81,12 @@ pub enum ConfigStrategy {
     Dhcp,
 }
 
-pub struct Stack<D: Device> {
+pub struct Stack<D: Driver> {
     pub(crate) socket: RefCell<SocketStack>,
     inner: RefCell<Inner<D>>,
 }
 
-struct Inner<D: Device> {
+struct Inner<D: Driver> {
     device: D,
     link_up: bool,
     config: Option<Config>,
@@ -102,7 +101,7 @@ pub(crate) struct SocketStack {
     next_local_port: u16,
 }
 
-impl<D: Device + 'static> Stack<D> {
+impl<D: Driver + 'static> Stack<D> {
     pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
         mut device: D,
         config: ConfigStrategy,
@@ -130,7 +129,7 @@ impl<D: Device + 'static> Stack<D> {
             b = b.routes(Routes::new(&mut resources.routes[..]));
         }
 
-        let iface = b.finalize(&mut DeviceAdapter {
+        let iface = b.finalize(&mut DriverAdapter {
             inner: &mut device,
             cx: None,
         });
@@ -211,7 +210,7 @@ impl SocketStack {
     }
 }
 
-impl<D: Device + 'static> Inner<D> {
+impl<D: Driver + 'static> Inner<D> {
     fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
         #[cfg(feature = "medium-ethernet")]
         let medium = self.device.capabilities().medium;
@@ -263,7 +262,7 @@ impl<D: Device + 'static> Inner<D> {
         s.waker.register(cx.waker());
 
         let timestamp = instant_to_smoltcp(Instant::now());
-        let mut smoldev = DeviceAdapter {
+        let mut smoldev = DriverAdapter {
             cx: Some(cx),
             inner: &mut self.device,
         };
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs
index 0dc8da73a..0fbf0c91b 100644
--- a/embassy-net/src/tcp.rs
+++ b/embassy-net/src/tcp.rs
@@ -3,12 +3,12 @@ use core::future::poll_fn;
 use core::mem;
 use core::task::Poll;
 
+use embassy_net_driver::Driver;
 use smoltcp::iface::{Interface, SocketHandle};
 use smoltcp::socket::tcp;
 use smoltcp::time::Duration;
 use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
 
-use crate::device::Device;
 use crate::{SocketStack, Stack};
 
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
@@ -66,7 +66,7 @@ impl<'a> TcpWriter<'a> {
 }
 
 impl<'a> TcpSocket<'a> {
-    pub fn new<D: Device>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
+    pub fn new<D: Driver>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
         let s = &mut *stack.socket.borrow_mut();
         let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };
         let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };
@@ -336,19 +336,19 @@ pub mod client {
     use super::*;
 
     /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ.
-    pub struct TcpClient<'d, D: Device, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> {
+    pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> {
         stack: &'d Stack<D>,
         state: &'d TcpClientState<N, TX_SZ, RX_SZ>,
     }
 
-    impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> {
+    impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> {
         /// Create a new TcpClient
         pub fn new(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self {
             Self { stack, state }
         }
     }
 
-    impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect
+    impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect
         for TcpClient<'d, D, N, TX_SZ, RX_SZ>
     {
         type Error = Error;
@@ -386,7 +386,7 @@ pub mod client {
     }
 
     impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> {
-        fn new<D: Device>(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> {
+        fn new<D: Driver>(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> {
             let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?;
             Ok(Self {
                 socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) },
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs
index 2f5334df3..0ee8c6e19 100644
--- a/embassy-net/src/udp.rs
+++ b/embassy-net/src/udp.rs
@@ -3,11 +3,12 @@ use core::future::poll_fn;
 use core::mem;
 use core::task::Poll;
 
+use embassy_net_driver::Driver;
 use smoltcp::iface::{Interface, SocketHandle};
 use smoltcp::socket::udp::{self, PacketMetadata};
 use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
 
-use crate::{Device, SocketStack, Stack};
+use crate::{SocketStack, Stack};
 
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -31,7 +32,7 @@ pub struct UdpSocket<'a> {
 }
 
 impl<'a> UdpSocket<'a> {
-    pub fn new<D: Device>(
+    pub fn new<D: Driver>(
         stack: &'a Stack<D>,
         rx_meta: &'a mut [PacketMetadata],
         rx_buffer: &'a mut [u8],
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 0c491ee46..67996cca4 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -39,7 +39,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
 embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]}
 embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
 embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
-embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true }
+embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
 embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
 
 embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
@@ -75,7 +75,7 @@ quote = "1.0.15"
 stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
 
 [features]
-defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"]
+defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"]
 sdmmc-rs = ["embedded-sdmmc"]
 memory-x = ["stm32-metapac/memory-x"]
 subghz = []
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs
index fd1b48530..9f62b61ee 100644
--- a/embassy-stm32/src/eth/mod.rs
+++ b/embassy-stm32/src/eth/mod.rs
@@ -1,14 +1,17 @@
 #![macro_use]
-#![cfg_attr(not(feature = "embassy-net"), allow(unused))]
 
 #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")]
 #[cfg_attr(eth_v2, path = "v2/mod.rs")]
 mod _version;
 pub mod generic_smi;
 
-pub use _version::*;
+use core::task::Context;
+
+use embassy_net_driver::{Capabilities, LinkState};
 use embassy_sync::waitqueue::AtomicWaker;
 
+pub use self::_version::*;
+
 #[allow(unused)]
 const MTU: usize = 1514;
 const TX_BUFFER_SIZE: usize = 1514;
@@ -40,92 +43,84 @@ impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
 
 static WAKER: AtomicWaker = AtomicWaker::new();
 
-#[cfg(feature = "embassy-net")]
-mod embassy_net_impl {
-    use core::task::Context;
+impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> {
+    type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
+    type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
 
-    use embassy_net::device::{Device, DeviceCapabilities, LinkState};
-
-    use super::*;
-
-    impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> {
-        type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
-        type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
-
-        fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
-            WAKER.register(cx.waker());
-            if self.rx.available().is_some() && self.tx.available().is_some() {
-                Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx }))
-            } else {
-                None
-            }
-        }
-
-        fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
-            WAKER.register(cx.waker());
-            if self.tx.available().is_some() {
-                Some(TxToken { tx: &mut self.tx })
-            } else {
-                None
-            }
-        }
-
-        fn capabilities(&self) -> DeviceCapabilities {
-            let mut caps = DeviceCapabilities::default();
-            caps.max_transmission_unit = MTU;
-            caps.max_burst_size = Some(self.tx.len());
-            caps
-        }
-
-        fn link_state(&mut self, cx: &mut Context) -> LinkState {
-            // TODO: wake cx.waker on link state change
-            cx.waker().wake_by_ref();
-            if P::poll_link(self) {
-                LinkState::Up
-            } else {
-                LinkState::Down
-            }
-        }
-
-        fn ethernet_address(&self) -> [u8; 6] {
-            self.mac_addr
+    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
+        WAKER.register(cx.waker());
+        if self.rx.available().is_some() && self.tx.available().is_some() {
+            Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx }))
+        } else {
+            None
         }
     }
 
-    pub struct RxToken<'a, 'd> {
-        rx: &'a mut RDesRing<'d>,
-    }
-
-    impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> {
-        fn consume<R, F>(self, f: F) -> R
-        where
-            F: FnOnce(&mut [u8]) -> R,
-        {
-            // NOTE(unwrap): we checked the queue wasn't full when creating the token.
-            let pkt = unwrap!(self.rx.available());
-            let r = f(pkt);
-            self.rx.pop_packet();
-            r
+    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
+        WAKER.register(cx.waker());
+        if self.tx.available().is_some() {
+            Some(TxToken { tx: &mut self.tx })
+        } else {
+            None
         }
     }
 
-    pub struct TxToken<'a, 'd> {
-        tx: &'a mut TDesRing<'d>,
+    fn capabilities(&self) -> Capabilities {
+        let mut caps = Capabilities::default();
+        caps.max_transmission_unit = MTU;
+        caps.max_burst_size = Some(self.tx.len());
+        caps
     }
 
-    impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> {
-        fn consume<R, F>(self, len: usize, f: F) -> R
-        where
-            F: FnOnce(&mut [u8]) -> R,
-        {
-            // NOTE(unwrap): we checked the queue wasn't full when creating the token.
-            let pkt = unwrap!(self.tx.available());
-            let r = f(&mut pkt[..len]);
-            self.tx.transmit(len);
-            r
+    fn link_state(&mut self, cx: &mut Context) -> LinkState {
+        // TODO: wake cx.waker on link state change
+        cx.waker().wake_by_ref();
+        if P::poll_link(self) {
+            LinkState::Up
+        } else {
+            LinkState::Down
         }
     }
+
+    fn ethernet_address(&self) -> [u8; 6] {
+        self.mac_addr
+    }
 }
+
+pub struct RxToken<'a, 'd> {
+    rx: &'a mut RDesRing<'d>,
+}
+
+impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> {
+    fn consume<R, F>(self, f: F) -> R
+    where
+        F: FnOnce(&mut [u8]) -> R,
+    {
+        // NOTE(unwrap): we checked the queue wasn't full when creating the token.
+        let pkt = unwrap!(self.rx.available());
+        let r = f(pkt);
+        self.rx.pop_packet();
+        r
+    }
+}
+
+pub struct TxToken<'a, 'd> {
+    tx: &'a mut TDesRing<'d>,
+}
+
+impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> {
+    fn consume<R, F>(self, len: usize, f: F) -> R
+    where
+        F: FnOnce(&mut [u8]) -> R,
+    {
+        // NOTE(unwrap): we checked the queue wasn't full when creating the token.
+        let pkt = unwrap!(self.tx.available());
+        let r = f(&mut pkt[..len]);
+        self.tx.transmit(len);
+        r
+    }
+}
+
 /// Station Management Interface (SMI) on an ethernet PHY
 ///
 /// # Safety
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs
index de36d3da1..9c0f4d66d 100644
--- a/embassy-stm32/src/eth/v1/mod.rs
+++ b/embassy-stm32/src/eth/v1/mod.rs
@@ -13,7 +13,7 @@ pub(crate) use self::rx_desc::{RDes, RDesRing};
 pub(crate) use self::tx_desc::{TDes, TDesRing};
 use super::*;
 use crate::gpio::sealed::{AFType, Pin as __GpioPin};
-use crate::gpio::{AnyPin, Speed};
+use crate::gpio::AnyPin;
 #[cfg(eth_v1a)]
 use crate::pac::AFIO;
 #[cfg(any(eth_v1b, eth_v1c))]
@@ -66,7 +66,7 @@ macro_rules! config_pins {
         critical_section::with(|_| {
             $(
                 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
-                $pin.set_speed(Speed::VeryHigh);
+                $pin.set_speed(crate::gpio::Speed::VeryHigh);
             )*
         })
     };
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index 1e72ce682..d0742ceb3 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -19,7 +19,7 @@ default = ["usbd-hid"]
 embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
 embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
 embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
-embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true }
+embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
 
 defmt = { version = "0.3", optional = true }
 log = { version = "0.4.14", optional = true }
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
index 60bbfd8d4..093afeff9 100644
--- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs
+++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
@@ -3,7 +3,7 @@ use core::mem::MaybeUninit;
 use core::task::Context;
 
 use embassy_futures::select::{select, Either};
-use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium};
+use embassy_net_driver::{Capabilities, LinkState, Medium};
 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
 use embassy_sync::blocking_mutex::Mutex;
 use embassy_sync::waitqueue::WakerRegistration;
@@ -108,7 +108,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
     ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) {
         let (tx_usb, rx_usb) = self.split();
 
-        let mut caps = DeviceCapabilities::default();
+        let mut caps = Capabilities::default();
         caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
         caps.medium = Medium::Ethernet;
 
@@ -158,11 +158,11 @@ pub struct Device<'d, const MTU: usize> {
     rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
     tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,
     link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
-    caps: DeviceCapabilities,
+    caps: Capabilities,
     ethernet_address: [u8; 6],
 }
 
-impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> {
+impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
     type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ;
     type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ;
 
@@ -184,7 +184,7 @@ impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> {
     }
 
     /// Get a description of device capabilities.
-    fn capabilities(&self) -> DeviceCapabilities {
+    fn capabilities(&self) -> Capabilities {
         self.caps.clone()
     }
 
@@ -205,7 +205,7 @@ pub struct RxToken<'a, const MTU: usize> {
     rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf<MTU>>,
 }
 
-impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> {
+impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> {
     fn consume<R, F>(mut self, f: F) -> R
     where
         F: FnOnce(&mut [u8]) -> R,
@@ -222,7 +222,7 @@ pub struct TxToken<'a, const MTU: usize> {
     tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf<MTU>>,
 }
 
-impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> {
+impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> {
     fn consume<R, F>(mut self, len: usize, f: F) -> R
     where
         F: FnOnce(&mut [u8]) -> R,
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs
index 2ee47f68c..4954a65bc 100644
--- a/embassy-usb/src/class/cdc_ncm/mod.rs
+++ b/embassy-usb/src/class/cdc_ncm/mod.rs
@@ -21,7 +21,6 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
 use crate::types::*;
 use crate::Builder;
 
-#[cfg(feature = "embassy-net")]
 pub mod embassy_net;
 
 /// This should be used as `device_class` when building the `UsbDevice`.
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml
index 54a477b49..994823a9e 100644
--- a/examples/nrf/Cargo.toml
+++ b/examples/nrf/Cargo.toml
@@ -16,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
 embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
 embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
 embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
-embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true }
+embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
 embedded-io = "0.4.0"
 embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true }
 
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 57cddd432..afd1042a1 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -10,7 +10,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
 embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
 embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] }
-embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] }
+embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
 embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index 649e39747..45b2a4a4f 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -9,6 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["lo
 embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] }
 embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] }
 embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] }
+embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" }
 embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] }
 critical-section = { version = "1.1", features = ["std"] }
 
diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs
index bb3e194cc..d918a2e62 100644
--- a/examples/std/src/tuntap.rs
+++ b/examples/std/src/tuntap.rs
@@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
 use std::task::Context;
 
 use async_io::Async;
-use embassy_net::device::{self, Device, DeviceCapabilities, LinkState};
+use embassy_net_driver::{self, Capabilities, Driver, LinkState};
 use log::*;
 
 pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
@@ -137,7 +137,7 @@ impl TunTapDevice {
     }
 }
 
-impl Device for TunTapDevice {
+impl Driver for TunTapDevice {
     type RxToken<'a> = RxToken where Self: 'a;
     type TxToken<'a> = TxToken<'a> where Self: 'a;
 
@@ -170,8 +170,8 @@ impl Device for TunTapDevice {
         })
     }
 
-    fn capabilities(&self) -> DeviceCapabilities {
-        let mut caps = DeviceCapabilities::default();
+    fn capabilities(&self) -> Capabilities {
+        let mut caps = Capabilities::default();
         caps.max_transmission_unit = self.device.get_ref().mtu;
         caps
     }
@@ -190,7 +190,7 @@ pub struct RxToken {
     buffer: Vec<u8>,
 }
 
-impl device::RxToken for RxToken {
+impl embassy_net_driver::RxToken for RxToken {
     fn consume<R, F>(mut self, f: F) -> R
     where
         F: FnOnce(&mut [u8]) -> R,
@@ -204,7 +204,7 @@ pub struct TxToken<'a> {
     device: &'a mut Async<TunTap>,
 }
 
-impl<'a> device::TxToken for TxToken<'a> {
+impl<'a> embassy_net_driver::TxToken for TxToken<'a> {
     fn consume<R, F>(self, len: usize, f: F) -> R
     where
         F: FnOnce(&mut [u8]) -> R,
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index a8b2baf29..b80dbbf9c 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
 embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
-embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"]  }
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"]  }
 embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
 embedded-io = { version = "0.4.0", features = ["async"] }
 
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 63e6caa77..d30c42b1f 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
 embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
-embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
 embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] }
 embedded-io = { version = "0.4.0", features = ["async"] }
 
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index ce999f67e..c0accb0d6 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -11,7 +11,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
 embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
 embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"]  }
-embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] }
+embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
 embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 usbd-hid = "0.6.0"