If you don't want the PC to detect your device (and try to restart it when the enumeration fails), why don't you simply disable the USB pull-up resistor? Most STM32s have it built-in and controlled by software. When that resistor is disabled, the host will not be able to detect that anything has been connected and no communication (and neither port reset by the host) will happen. That's the canonical way to implement USB device connection/disconnection without power cycling.
But if I understood that part correctly I can't detect that is the host from device side too?
You might be able to detect that you are connected to a real, talking, host and not a charger - but by the time you do so it is too late because the host has already started the enumeration process. And if your device doesn't enumerate properly at that stage by sending proper descriptors, etc., it will make the host to try to reset the port, power cycle your device and ultimately report an error to the user.
You can't do anything about that from the device side. Full stop.It is as simple as that. The moment you have put that pull-up resistor on the bus you are committed because USB is entirely host-driven. The only way to stop the communication is by disconnecting from the bus, which will likely cause an error message to be shown to the user.
So if your goal is to prevent the host from power cycling your device at an inopportune moment because you weren't ready to communicate yet, you can't do it by trying to detect when the enumeration starts - at that point the clock is ticking already and it is too late.
I'm utilizing an RTOS, which allows me to employ a dedicated thread for monitoring the SOF status. This means that until the PC initiates communication, the device can behave as if it were connected to a charger.
That's completely irrelevant because if your device does not enumerate within few hundred ms or so of putting that resistor on the bus (and receiving SOF means the host has detected that already), it will get a bus reset and possibly power cycle,
no matter what you do on the device side with your RTOS and threads. If you don't want to talk to the host, don't put those pull-up resistors on the bus. The proper way to achieve what you want would be to leave the USB interface off, try to check whether the two data pins are shorted, e.g. checking whether driving one with a small voltage (do not put 3.3V on the pin/drive it high - that will start the enumeration, the same as connecting a pull-up!) drives the other pin up too. If it does, they are likely shorted and you have a charger. If it does not, assume a host, do whatever you need to do and
only when you are ready to communicate initialize the USB peripheral and connect the pull-up resistor to the bus.
The host side has 15k pull-down resistors on the D+/D- lines, so they are normally idling low when there is nothing connected. Chargers short the data lines together and leave them floating.
The other way to solve this is to always enumerate as some dummy device when you are connected. When that happens, you know you are talking to a host, so you do your housekeeping, disconnect from the bus by disconnecting the pull-up to force re-enumeration, then reconnect the resistor to the bus to signal new device connected. Finally reply to the new enumeration request as the real device you want to be seen as. This is very common with devices that have bootloaders for firmware update, for example like that Cypress FX2. The disadvantage of this is that you will often get two "device connected" sounds from the computer and it may confuse some users if they see some weird device connecting, then disconnecting and a new device connecting.
Those two ways are the only methods to achieve what you want reliably. Messing with the SOF and trying to beat the USB host by reconfiguring your code while it is waiting for your enumeration response is bound to be fragile and unreliable.
However, do any of you have an idea as to why some ports allow me to halt communication? I mean, I'm using the same PC with two different USB ports. On one of them, everything works perfectly, but when I attempt to use the other, I observe a VBUS reset.
That completely depends on the host, the capabilities of the hubs you are connected to, etc. Some are capable of power cycling the ports, some are not. Also the driver may be more or less "aggresive" at trying to recover the communication - some drivers/hosts could be content with just forcing a bus reset (driving both data lines low) to force a re-enumeration. Some others may also try to power cycle the port as a last resort. Either way, that's not a behavior you can rely on.