Finally got the LED to light up. There was a lot of printing every register read & write to compare with the kernel driver. The mane differences were because this board was being reset by every microcontroller reset, while reloading the kernel module didn't reset its board.
After trying many random things, a further problem with the stm32 USB stack was revealed. The board wasn't taking up all the register writes with CTRL_STATUS_IN bypassed. The only workaround that worked, after many random ideas, was reading back every write. It's slow, but better than nothing.
As predicted, the STM32 USB driver continued to give problems. There's a sequence in USBH_HandleControl when sending data to the device on the config endpoint:
CTRL_SETUP -> CTRL_SETUP_WAIT -> CTRL_DATA_OUT -> CTRL_DATA_OUT_WAIT ->
CTRL_STATUS_IN -> CTRL_STATUS_IN_WAIT
which locks up in CTRL_STATUS_IN_WAIT. Another 2 days yielded a workaround in skipping CTRL_STATUS_IN -> CTRL_STATUS_IN_WAIT & it works well enough.