In other words, how I went from hardware limits to a custom firmware
Hey! It’s Rob again, and this is part two of the Joyo MOMIX Cab reverse engineering.

In the first part, I took a look at the hardware of the Joyo MOMIX Cab and quickly realized that disabling direct monitoring was not possible at the hardware level. Everything happens inside the DSP.
This second part is where things finally worked.
I managed to dump the original firmware. I tried to decompile it, without much luck. At that point, I changed approach. Using the official SDK, I wrote a new firmware from scratch and flashed it onto the device.
The result is exactly what I was hoping for. Direct monitoring is gone, and the MOMIX Cab now behaves properly when used for guitar recording.
Before getting into the details, a few updates since the last post.
I didn’t get any response from either JL or JOYO, which I honestly expected. The good news is that the JL USB Updater finally arrived, and that allowed me to read and dump the flash memory of the device. Around the same time, the seller was kind enough to send me some documentation related to the JL AC6926A4, which helped clarify a few details about the platform.
I also received the logic analyzer and the extra switch buttons, just in case they turn out to be useful later on.
In the meantime, I spent some time tracing the PCB connections of the MOMIX Cab. Here’s what I’ve mapped so far:
- PB0: “Aux” switch input, the unpopulated button on the board
- PB1: Volume button minus
- PB2: Volume button plus
- PC4: Green LED
- PC5: Red LED
- MIC0 / PA0: audio input path
I’ll probably try to draw a proper schematic as well, to make the project as complete as possible.
While digging around, I also found out that the MOMIX Cab appears to be a variation of the Kewee Skyrecorder. There are also other devices that look essentially identical and are sold under different names, such as the Ueteto AI-8.
In this post, I’ll go through what worked, what didn’t, and how I eventually got there.
Let’s get to work.
Step 1 – Using jl-uboot-tool
The jl-uboot-tool is a utility developed by kagaimiq, who also wrote most of the available documentation on JL chips. The tool connects to the device once it has been put into UBOOT mode using the JL USB Updater.

Once connected, a CLI appears, allowing the user to read, dump, or write the firmware. The device enters UBOOT mode simply by pressing the “update” button on the loader, which sends a special USB signal instructing the device to switch into UBOOT mode.
For more details, check the in-depth guide by kagaimiq.
Step 2 – Flash dump and Decompiling
After I was able to connect to the device, I had to enter the correct command to dump the firmware. The command is “read”, but it requires a start address and an end address. To calculate this, I used the full flash memory size of the chip under test, the AC6926A4. The “4” in the chip code stands for 4 Mbit, which divided by 8 gives 512 KB. In hexadecimal, this is 0x80000:
AC6926A4 = 4 Mbit = 512 KiB = 0x80000 bytes
The complete command I used was:
read 0x0000 0x80000 MomixCab.bin
With this, the firmware was successfully dumped.
The next step was to unpack and decrypt the firmware using the firmware tools from jl-misctools. From the extracted files, the following were obtained:
bt_cfg.binf_ascii_s.pixsdk.app

The file of our interest is sdk.app, which contains the main firmware code for the device.
Note: If you encounter a problem with UTF encoding when running the scripts:

You can fix it by editing the Python script and adding the following lines at the beginning:
import sysimport iosys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
After this change, the output should display correctly.
Step 3 – Disassembly and Decompiling attempt
The first thing that came to my mind was to try to disassemble and decompile the firmware, in the hope of finding something useful, such as a parameter that could be changed to disable the dry monitoring, maybe by patching the firmware directly.
I tried several tools:
- IDA
- Ghidra
- Binary Ninja
- Hexview (which later turned out to be useful)
That said, I am not a reverse engineer, so this quickly turned into a dead-end for me.
Still, it was interesting to explore different ways of looking at binary files and to get a rough idea of how the firmware is structured.
After that, I started looking for more information about JieLi chips in general and eventually stumbled upon the USMI forum. There, I found out that building a firmware from the official SDK and flashing it onto the device was actually more approachable than I initially thought.
At that point, I started digging deeper into the SDK workflow and gradually realized that writing a new firmware for the device was not only feasible, but also the simplest and cleanest way forward.
Step 4 – The JieLi SDK and the new MOMIX Firmware
I started studying the JL SDK and after a lot of trial and error, I was able to write a new firmware for the device.
I will explain all the steps of editing the SDK base project, separated by the section they belong to:
Main Config File: sdk_cfg.h
- Set
USB_PC_EN = 1. This ensures that the device is in USB mode. - Set
USB_SD0_MULT_EN = 0andUSB_SD1_MULT_EN = 0. This disables the SD card modules. - Force the clock to 192 MHz by setting
SYS_Hz = 192000000L. This ensures the clock is at its maximum rate and prevents audio drops and corruption. - Disable Bluetooth by setting
BLE_BREDR_MODE = 0. This ensures Bluetooth is always off. - Verify that unnecessary features are disabled (for example,
ECHO_EN=0,REC_EN=0,FM_RADIO_EN=0). This reduces firmware size and ensures no other tasks run in parallel.
Audio Parameters: DAC, LADC, Mic Gain & Attenuation
- In
audio_param.h, set the LADC buffer to 32 samples withDAC_SAMPLE_CON = 0. This was the only setting that worked:0-> USB device works correctly1-> audio crackling2-> continuous pop (chip tries to start but cannot)3-> device won’t turn on (probably asking for too much RAM) - In
audio.h, increase the DAC buffer to 9216 bytes by modifyingOUTPUT_BUF_SIZEto(576*2*2*4). This ensures output audio stability. - In
ladc.c, add the functionladc_mic_gain_atten(u8 gain, u8 gx2, u8 neg12_en)after the existingladc_mic_gain()function. This solves the problem of the input being too hot; -12 dB attenuation was the perfect solution.
Board Parameters: Tasks, LEDs, Inits
- In
led.h, replace the entire#elif(LED_TYPE_SEL == LED_PXX)section. This initializes the on-board LEDs. - In
board_init.c, uncomment the call toset_port_init(). This ensures the ports are initialized. - In
board_init.c, afterset_port_init(), add the callsLED_INIT_EN();andB_LED_ON();. This ensures the power-on LED is on from the start. - In
board.c, correct the task startup by replacingTASK_ID_BTwithTASK_ID_PCin thetask_switch()call. This ensures the task manager runs the correct task, i.e., the PC task.
PC Device Configuration: USB User Descriptor
- In
dev_pc.c, replace the product stringUSER_RIPRODUCT_STRwith"MomixCabMOD", ensuring its total length is set to 24 bytes. This is the name visible from the drop-down volume menu. - In
dev_pc.c, replace the manufacturer stringUSER_IMANUFACTURE_STRwith"JOYO Technology", setting the first byte to0x22(34 bytes total). This restores the original manufacturer name. - In
dev_pc.c, inside the MIC section ofapp_usb_slave_init(), afterladc_ch_open(), add the lineladc_mic_gain_atten(0x02, 0, 1);. This ensures the mic attenuation is correctly set.
I built the project, and the flashing started automatically.

The device was recognized by the PC, and after some testing, it was fully functional! No dry-monitor in sight, just a plain USB audio interface with a mono input and stereo output.
A problem appeared when connecting the device to a portable device: the error “the device is drawing too much current” showed up. After investigating, I found that the issue was caused by the USB_Descriptor class, in particular the bMaxPower field. Portable devices provide 100 mA of current, so adjustments were needed to make it compatible.
Step 5 – Reverse Engineering the USB Protocol and Patching
USB descriptors are data structures used by USB devices to describe their capabilities and requirements to the host. They include information such as device type, manufacturer, product name, supported configurations, endpoints, and power requirements.
When a USB device is connected, the host requests these descriptors using standard USB requests, like GET_DESCRIPTOR and GET_CONFIGURATION. The data returned is a sequence of bytes, for example:
09 02 00 00 ...
Each byte or group of bytes has a specific meaning according to the USB specification. In this example, 09 indicates the descriptor length in bytes, 02 identifies the descriptor type (configuration descriptor), and the following bytes describe attributes such as total length, number of interfaces, and other parameters.
By analyzing these descriptors, it is possible to see how the device presents itself, what interfaces it offers, and how much current it claims to draw. In our case, examining the USB descriptors was crucial to identify the excessive power draw reported by portable devices and to patch the firmware accordingly.
By connecting the device to my PC and using Wireshark, I analyzed the GET DESCRIPTOR Response CONFIGURATION packet and found that the device had a 400 mA power limit, which is too high for our use case.

The bMaxPower field multiplies 2 mA by its value, giving the maximum current parameter.
Unfortunately, after some investigation, it was not possible to edit this parameter from the SDK, as it is probably hardcoded inside a library.
It was time to open the sdk.app file and patch it manually. Using the original sdk.app as a reference, I confirmed that it was set at a 100 mA limit (hex 0x32 = decimal 50 × 2 mA).

I then opened my compiled sdk.app and searched for the bytes that differed.

By directly modifying the binary file with a hex editor, I located the sequence 80 C8 (hex 0xC8 = decimal 200 × 2 mA = 400 mA) within the configuration descriptor and replaced it with 80 32 to set the maximum current draw to 100 mA.

After saving the file, I crossed my fingers, hoping there wouldn’t be any CRC checks during flashing.
I then launched the download.bat file, and everything went smoothly
I connected the Momix, launched a Wireshark capture, and voilà! The device was recognized and now set at 100 mA maximum power.

For the final test, I connected it to my iPad and, hell yeah, I could record with GarageBand perfectly!
Step 6 – Conclusion: Future Work and Added Functions
I finally did what I wanted to do and what started as a personal challenge, pushing my abilities: remove the dry monitor function! During the process, I encountered some issues and tried to push the device as far as I could, keeping in mind that I only had access to an SDK and not a full programming environment. But I can say I’m happy with how everything turned out.
The Momix is working well, but there’s still a little to-do list for this project:
- The buttons are unused, so right now I have three (two stock + one added) buttons that can be mapped to functions.
My ideas for the button functions:- Toggle dry monitor on/off (maybe someone will need it)
- Toggle attenuation on/off
- Input gain setting
- The red LED is unused at the moment. It would be great to implement the input clipping indicator from the original device (the light turns red when the input is clipping).
- Find a way to flash the firmware without having to buy the JL USB Updater, possibly using a solution based on the Arduino implementation by Christian Kramer
- Create an app that can interface with the device to set parameters.
For now, the main goal is done, so the project will take a pause. Any other experiments or improvements will happen little by little, whenever I find some free time.
For now, that’s all, folks!
I’ll see you later ✌️🏻
Acknowledgements
Many thanks to:
- kagaimiq, for the JL chip documentation and jl-uboot-tool
- USMI forum (JL SoC thread), for insights and guidance on firmware building
- Christian Kramer, for valuable tips and support
Complete project available on my GitHub







