Why is the USB Interface not working?

Hello Everyone!
It's my first post here. I'm trying to learn Rust programming for embedded devices, and I'd like to set up USB communication for my Nucleo F413ZH board.
I've managed to set up everything correctly and I've tested a simple Blink program, which is working. I've tried moving on to USB communication, writing the following two files:

main.rs

#![deny(unsafe_code)]
#![allow(clippy::empty_loop)]
#![no_main]
#![no_std]

mod usb;

// dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`
#[cfg(debug_assertions)]
use panic_halt as _;

// release profile: minimize the binary size of the application
#[cfg(not(debug_assertions))]
use panic_abort as _;

use cortex_m_rt::entry;
use stm32f4xx_hal as hal;
use crate::hal::{pac, prelude::*};

fn initialize_clocks(rcc: hal::rcc::Rcc) -> hal::rcc::Clocks {
    return rcc
        .cfgr
        // We will use the external 8MHz HSE oscillator
        .use_hse(8.MHz())
        // We will run the internal clock at 64MHz
        .sysclk(64.MHz())
        .pclk1((8).MHz())
        .freeze();
}

use stm32f4xx_hal::otg_fs::{USB};

#[entry]
fn main() -> ! {

    let dp = pac::Peripherals::take().unwrap() ;
    let cp = cortex_m::Peripherals::take().unwrap();

    // Initialize clocks
    let clocks = initialize_clocks(dp.RCC.constrain());

    // Initialize USB
    let gpioa = dp.GPIOA.split();
    let usb_configuration = USB {
        usb_global: dp.OTG_FS_GLOBAL,
        usb_device: dp.OTG_FS_DEVICE,
        usb_pwrclk: dp.OTG_FS_PWRCLK,
        pin_dm: gpioa.pa11.into_alternate(),
        pin_dp: gpioa.pa12.into_alternate(),
        hclk: clocks.hclk(),
    };
    usb::initialize_usb(usb_configuration);

    // Create an the LED abstraction. On the Nucleo-401RE it's connected to pin PA5.
    let gpiob = dp.GPIOB.split();
    let mut led = gpiob.pb7.into_push_pull_output();

    // Create a delay abstraction based on SysTick
    let mut delay = cp.SYST.delay(&clocks);

    loop {
        // On for 1s, off for 1s.
        led.set_high();
        delay.delay_ms(1000_u32);
        led.set_low();
        delay.delay_ms(1000_u32);
    }
}

usb.rs

#![allow(unsafe_code)]

use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use stm32f4xx_hal::otg_fs::{UsbBus, USB};
use stm32f4xx_hal::pac::{interrupt, Interrupt};
use usb_device::class_prelude::UsbBusAllocator;
use usb_device::prelude::*;
use usbd_serial::SerialPort;

// Make USB serial device globally available
static G_USB_SERIAL: Mutex<RefCell<Option<SerialPort<UsbBus<USB>>>>> =
    Mutex::new(RefCell::new(None));

// Make USB device globally available
static G_USB_DEVICE: Mutex<RefCell<Option<UsbDevice<UsbBus<USB>>>>> =
    Mutex::new(RefCell::new(None));

pub fn initialize_usb(usb: USB) {
    static mut EP_MEMORY: [u32; 1024] = [0; 1024];
    static mut USB_BUS: Option<UsbBusAllocator<stm32f4xx_hal::otg_fs::UsbBusType>> = None;

    unsafe {
        USB_BUS = Some(stm32f4xx_hal::otg_fs::UsbBusType::new(usb, EP_MEMORY.as_mut()));
        let usb_bus = USB_BUS.as_ref().unwrap();

        cortex_m::interrupt::free(|cs| {
            *G_USB_SERIAL.borrow(cs).borrow_mut() = Some(SerialPort::new(usb_bus));

            *G_USB_DEVICE.borrow(cs).borrow_mut() = Some(
                UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
                    .manufacturer("Fake company")
                    .product("Serial port")
                    .serial_number("TEST")
                    .device_class(usbd_serial::USB_CLASS_CDC)
                    .build(),
            );
        });

        cortex_m::peripheral::NVIC::unmask(Interrupt::OTG_FS);
    }
}

#[interrupt]
unsafe fn OTG_FS() {
    static mut USB_SERIAL: Option<SerialPort<UsbBus<USB>>> = None;
    static mut USB_DEVICE: Option<UsbDevice<UsbBus<USB>>> = None;

    let usb_dev = USB_DEVICE.get_or_insert_with(|| {
        cortex_m::interrupt::free(|cs| {
            // Move USB device here, leaving a None in its place
            G_USB_DEVICE.borrow(cs).replace(None).unwrap()
        })
    });

    let serial = USB_SERIAL.get_or_insert_with(|| {
        cortex_m::interrupt::free(|cs| {
            // Move USB serial device here, leaving a None in its place
            G_USB_SERIAL.borrow(cs).replace(None).unwrap()
        })
    });

    if usb_dev.poll(&mut [serial]) {
        let mut buf = [0u8; 64];

        match serial.read(&mut buf) {
            Ok(count) if count > 0 => {
                // Echo back in upper case
                for c in buf[0..count].iter_mut() {
                    if 0x61 <= *c && *c <= 0x7a {
                        *c &= !0x20;
                    }
                }

                let mut write_offset = 0;
                while write_offset < count {
                    match serial.write(&buf[write_offset..count]) {
                        Ok(len) if len > 0 => {
                            write_offset += len;
                        }
                        _ => {}
                    }
                }
            }
            _ => {}
        }
    }
}

However, whenever I run lsusb in my terminal, after connecting the board to my PC, nothing shows up. I should be able to hear a notification and see a Fake Company Serial Port usb device.
Why is that (not) happening?

Thank you very much,
Riccardo

Haven't looked into your code deeply as I'm drowning in my own blood pool right now :smile:

But this template repo could help as it has a working USB device implementation for CDC class.

Let me know about the progress.

Cheers,

Here's a youtube video that might help: