For usb rather than using 'libraries', at some point one should go back to 'first principles' to figure out how to implement a usb protocol from scratch.
this would help you understand the codes at least.
the usb protocols are mostly documented at
https://www.usb.org/documents
it partly has to do with 'device descriptors', 'device descriptors' tells the host what to expect of the device.
Normally, there is a hierarchy where you have the device and within each device there can be multiple interfaces, so there could be multiple different device/interface descriptors.
device/interface descriptors are merely a bunch of data, you could literally hard code it in structures and send it when a get_descriptor usb request is received.
but 'descriptors' alone merely describe the interface. so the next thing is you'd need to respond on the relevant usb endpoints that you declared your descriptors for. this is the usb stack that ST provides a driver for as well.
if you have a usb sniffer, e.g. wireshark
https://wiki.wireshark.org/CaptureSetup/USB
you could observe how the comms occur and how the descriptors are declared. the device class specs can be found on
https://www.usb.org/documents
normally, i've tried it and for usb-serial (cdc acm) there are basically 3 bulk in out endpoints used, one endpoint is IN for sending data to the host, one is OUT for receiving data from the host. and a 3rd i think is a control endpoint.
if you need to implement multiple serial connections, more of the sets of similar endpoints is needed.
you would also need an implementation to handle those multiple endpoints. i'm not sure how that is done in usb-composite, you may like ot use these as clues and review the codes.